最近,小编在知乎上看到这样一个问题:
请问你见过最惊艳的SQL查询语句是什么?
问题下面,各路大佬的回复精彩纷呈,小编精选了几个高赞回答,看完忍不住拍大腿感叹:SQL还能这样写?
1号网友:ByteFixer
可能是这个吧,用SQL写的俄罗斯方块,我声明一下,这个不是我写的,是Github上一个大佬的代码。
就是这个游戏:

来看SQL写的版本:

代码:
-- Copyright (c) 2024 nuno-faria---- This software is released under the MIT License.-- https://opensource.org/licenses/MIT--- Prerequisites ----- Table to store the inputs. cmd stores the key; ts stores the timestamp when the key was pressed.-- It is created outside of the query to make it available to the user.-- Marking it as "UNLOGGED" means it is not logged to the WAL, making writes faster.-- Possible cmd values:-- 'u' - rotate, 'd' - move down, 'l' - move left, 'r' - move right, 's' - full drop, 'p' - pauseCREATE UNLOGGED TABLE IF NOT EXISTS Input (cmd char, ts timestamp);TRUNCATE Input;INSERT INTO Input VALUES ('', now());-- Function to write a string to the console/log, using Postgres' RAISE command. The command is not-- available in regular SQL code, so this is the one time where plpgsql must be used. This function-- is necessary to render the game since Postgres only starts showing a recursive CTE's output when-- it is fully completed.CREATE OR REPLACE FUNCTION notify(str varchar) RETURNS void AS $$BEGINRAISE NOTICE '%', str;END$$ LANGUAGE PLPGSQL;-- dblink is a Postgres extension that allows querying a remote database. In this case, it is used-- to connect to the current database and read the most up to date information in the Input table,-- which would otherwise not be possible. While Postgres supports Read Committed (default isolation)-- for transactions, only a single query is executed here. For single queries, the isolation-- behavior adheres to Snapshot semantics, meaning reads to the Input table will be the same as if-- they were executed at the start of the query.CREATE EXTENSION IF NOT EXISTS dblink;--- Tetris game query ----- Main game loop implemented with a recursive Common Table Expression (CTE) query. The loop is-- designed to run until a piece clashes with another on spawn (i.e., game over).WITH RECURSIVE main AS (-- Constant parametersWITH const AS (SELECT-- board width10 AS width,-- board height20 AS height,-- frames per second the game loop runs at60 AS fps,-- initial interval at which a piece drops one line, i.e., gravity (seconds)48/60.0 AS init_drop_delta,-- minimum interval between piece drops (seconds)6/60.0 AS min_drop_delta,-- amount to decrease the drop interval per each level (seconds)2/60.0 AS drop_delta_decrease,-- number of lines to clear to increase one level10 AS lines_per_level,-- weight given to the current level in the earned points, according to the formula:-- base_points * (max(1, level * level_score_multiplier)). when set to 0, the level has-- no weight on the points earned1 AS level_score_multiplier),-- Number of points awarded based on the number of lines cleared in the same move (base_points)points_per_line(lines, points) AS (SELECT *FROM (VALUES(0, 0),(1, 100),(2, 300),(3, 500),(4, 800)) _),-- Set of pieces/tetrominoes-- id identifies a piece, without rotation, from 0 to 6-- rotation defines a rotation of a piece, from 0 to 3 (based on Nintendo Rotation System)-- piece is an array storing the initial coordinates of a piece in the board. the board-- coordinates are represented by a sequential integer from 0 to (const.width + 1) *-- const.height, where 0 is the cell at the top-left corner (each level actually has-- const.width + 1 cells, more details later).-- for example, [4, 5, (const.width+1) + 4, (const.width+1) + 5] represents a square piece-- in the middle of the first and second lines of the board.tetromino(id, rotation, piece) AS (SELECT id, rotation, pieceFROM const c(w), LATERAL (VALUES-- O(0, 0, ARRAY[4, 5, (c.w+1) + 4, (c.w+1) + 5]),(0, 1, ARRAY[4, 5, (c.w+1) + 4, (c.w+1) + 5]),(0, 2, ARRAY[4, 5, (c.w+1) + 4, (c.w+1) + 5]),(0, 3, ARRAY[4, 5, (c.w+1) + 4, (c.w+1) + 5]),-- I(1, 0, ARRAY[3, 4, 5, 6]),(1, 1, ARRAY[-(c.w+1) + 4, 4, 1*(c.w+1) + 4, 2*(c.w+1) + 4]),(1, 2, ARRAY[3, 4, 5, 6]),(1, 3, ARRAY[-(c.w+1) + 4, 4, 1*(c.w+1) + 4, 2*(c.w+1) + 4]),-- T(2, 0, ARRAY[3, 4, 5, (c.w+1) + 4]),(2, 1, ARRAY[-(c.w+1) + 4, 3, 4, (c.w+1) + 4]),(2, 2, ARRAY[-(c.w+1) + 4, 3, 4, 5]),(2, 3, ARRAY[-(c.w+1) + 4, 4, 5, (c.w+1) + 4]),-- L(3, 0, ARRAY[3, 4, 5, (c.w+1) + 3]),(3, 1, ARRAY[-(c.w+1) + 3, -(c.w+1) + 4, 4, (c.w+1) + 4]),(3, 2, ARRAY[-(c.w+1) + 5, 3, 4, 5]),(3, 3, ARRAY[-(c.w+1) + 4, 4, (c.w+1) + 4, (c.w+1) + 5]),-- J(4, 0, ARRAY[3, 4, 5, (c.w+1) + 5]),(4, 1, ARRAY[-(c.w+1) + 4, 4, (c.w+1) + 3, (c.w+1) + 4]),(4, 2, ARRAY[-(c.w+1) + 3, 3, 4, 5]),(4, 3, ARRAY[-(c.w+1) + 4, -(c.w+1) + 5, 4, (c.w+1) + 4]),-- S(5, 0, ARRAY[4, 5, (c.w+1) + 3, (c.w+1) + 4]),(5, 1, ARRAY[-(c.w+1) + 4, 4, 5, (c.w+1) + 5]),(5, 2, ARRAY[4, 5, (c.w+1) + 3, (c.w+1) + 4]),(5, 3, ARRAY[-(c.w+1) + 4, 4, 5, (c.w+1) + 5]),-- Z(6, 0, ARRAY[3, 4, (c.w+1) + 4, (c.w+1) + 5]),(6, 1, ARRAY[-(c.w+1) + 5, 4, 5, (c.w+1) + 4]),(6, 2, ARRAY[3, 4, (c.w+1) + 4, (c.w+1) + 5]),(6, 3, ARRAY[-(c.w+1) + 5, 4, 5, (c.w+1) + 4])) _(id, rotation, piece)),-- Connect to the local database with dblink once at the start of the query, to later read the-- the Input table. If the connection already exists, skips the creation.conn(name, _) AS (SELECT 'conn',CASE-- connection existsWHEN ARRAY['conn'] <@ dblink_get_connections() THEN ''-- connection does not existELSE dblink_connect('conn', 'dbname=' || current_database())END)-- Non-recursive term of the main loop, i.e., the initial stateSELECT-- frame0 AS frame,-- board: boolean 1d array where each position states if a cell is occupied or not. in-- addition to the regular playable const.width cells in each line, there is a extra cell at-- the end that is always occupied, to allow the side limits to be determined in a 1d array.-- 1d arrays are used instead of 2d as they are easier to work with in Postgres.string_to_array(repeat(repeat('f', const.width) || 't', const.height), NULL)::bool[] AS board,-- score0 AS score,-- number of lines cleared0 AS lines,-- drop deltaconst.init_drop_delta AS drop_delta,-- position information, storing the piece id, the rotation, the number of cells it has-- moved (where 0 is the default position), and the piece status:-- 1 - piece was dropped, either naturally or by user input, notifying that the piece-- might have reached the end-- 2 - new piece spawn, notifying that the next piece needs to be generated-- 0 - every other case, nothing to do(SELECT ARRAY[id, 0, 0, 0]FROM tetrominoORDER BY random()LIMIT 1) AS pos,-- number of lines a piece can be dropped. is used to simulate where the piece is going to-- land, to allow hard drops, and to determine game over (max_drop_lines = -1)0 AS max_drop_lines,-- next piece to spawn, to allow next piece preview(SELECT idFROM tetrominoORDER BY random()LIMIT 1) AS next_piece,-- last time a piece was dropped, either naturally or by user input. when the last_drop_time-- + drop_delta >= current time, the piece falls naturally. clock_timestamp() is used here-- and throughout the query since now() is transactional, i.e., reflects the time at the-- start of the queryclock_timestamp() AS last_drop_time,-- last registered input time, to execute each input only onceclock_timestamp() AS last_input_time,-- rendernotify('start'),-- sleeppg_sleep(0),-- last frame time, so the next sleep can be set in a way that matches the specified fpsclock_timestamp() AS last_frame_timeFROM constUNION ALL-- Recursive term, called at each frame.-- It starts by first reading the user input. Then, it processes the piece movement, updating-- the board, score, how far a piece can drop, and so on. Next, it renders the current state,-- using the notify function. Finally, it performs a sleep to match the specified fps.SELECT-- framemain.frame + 1,-- boardnext_board.board,-- scoremain.score + next_board.earned_points,-- number of lines clearedmain.lines + next_board.lines_cleared,-- drop delta based on current levelgreatest(const.min_drop_delta,const.init_drop_delta- const.drop_delta_decrease * ((main.lines + next_board.lines_cleared) / const.lines_per_level)),-- piece position (set the last element to 0 to reset the piece status in the next frame)movement.pos[:3] || ARRAY[0],-- max drop linesdrop_piece.lines,-- next piece idnext_piece.id,-- last drop timemovement.drop_time,-- last input timemovement.input_time,-- rendernotify(render.string),-- sleep the required amount to match the fps. the longer the time it takes to compute a-- frame, the less it needs to sleeppg_sleep(extract(epoch FROMmain.last_frame_time + make_interval(secs => 1 / const.fps::decimal) - clock_timestamp())),-- last frame timeclock_timestamp()FROM main,const,conn,-- retrieve the user input; the current frame is appended to the query to avoid it to be-- cached by the optimizerdblink(conn.name, 'SELECT * FROM Input --' || main.frame) input (cmd char, ts timestamp),-- compute the new position based on the user input. the LATERAL join allows each row of the-- previous relation (in this case, there is only one row) to be used inside the subqueryLATERAL (-- next position of the piece, based on the user input / natural fallWITH next_pos(pos, drop_time, input_time) AS (-- check if its time for the piece to fall naturallyWITH natural_fall(natural_fall) AS (SELECT main.last_drop_time + make_interval(secs => main.drop_delta) <= clock_timestamp()AND input.cmd <> 'p' AS natural_fall -- if paused, do not move)SELECT-- positionCASE-- natural fall, increase the position by one lineWHEN natural_fall THENmain.pos[:2] || ARRAY[main.pos[3] + const.width + 1] || 1-- user inputWHEN input.ts > main.last_input_time THENCASEWHEN input.cmd = 'u' THEN main.pos[:1] || ARRAY[(main.pos[2] + 1) % 4] || main.pos[3:]WHEN input.cmd = 'd' THEN main.pos[:2] || ARRAY[main.pos[3] + const.width + 1] || 1WHEN input.cmd = 'l' THEN main.pos[:2] || ARRAY[main.pos[3] - 1] || main.pos[4]WHEN input.cmd = 'r' THEN main.pos[:2] || ARRAY[main.pos[3] + 1] || main.pos[4]WHEN input.cmd = 's' THENmain.pos[:2] || ARRAY[main.pos[3] + main.max_drop_lines * (const.width + 1)] || 1END-- nothing to do, position stays the sameELSEmain.posEND AS pos,-- last_drop_timeCASE-- piece movedWHEN natural_fall OR (input.ts > main.last_input_time AND input.cmd = 'd') THENclock_timestamp()-- when a piece is hard-dropped, ensure that there is a natural drop in the-- next frame, to make the next piece appear fasterWHEN (input.ts > main.last_input_time AND input.cmd = 's') THENmain.last_drop_time - make_interval(secs => main.drop_delta)-- nothing to doELSEmain.last_drop_timeEND AS drop_time,-- last_input_time. only update it if the input was processed. this avoids the-- input being skipped when the natural fall occurs in the same frameCASEWHEN NOT natural_fall THENinput.tsELSEmain.last_input_timeEND AS input_timeFROM natural_fall),-- compute the new piece based on the next positionpiece_after_movement(new_piece) AS (SELECT array_agg(cell)::integer[] AS new_pieceFROM (SELECT unnest(piece) + next_pos.pos[3] AS cellFROM tetromino, next_posWHERE id = next_pos.pos[1]AND rotation = next_pos.pos[2]) _-- check if the new piece collides with any filled cell in the board), collision(collides) AS (SELECT bool_or(cell) AS collidesFROM unnest(main.board) WITH ORDINALITY b(cell, ordinality)JOIN unnest((SELECT new_piece FROM piece_after_movement)) p(coord)ON p.coord + 1 = b.ordinality)-- check if the next position is validSELECT drop_time, input_time,CASE-- new piece is in a valid placeWHEN-- no block reached the end(NOT new_piece && ARRAY(SELECT (const.width + 1) * const.height + iFROM generate_series(0, const.width + 1) _(i)))-- no block in the -1 or in the -(width + 1) - 1 positionsAND (NOT new_piece && ARRAY[-1]) AND NOT (new_piece && ARRAY[-(const.width + 1) - 1])-- no block clashes with filled cells in the boardAND (NOT collision.collides) THENnext_pos.pos-- new piece reached the end or it clashes with another block moving down ->-- spawn a new pieceWHEN next_pos.pos[4] = 1AND (new_piece && ARRAY(SELECT (const.width + 1) * const.height + iFROM generate_series(0, const.width + 1) _(i))OR collision.collides) THENARRAY[main.next_piece, 0, 0, 2]-- not a valid movement and did not reach the end, keep the same positionELSEmain.posEND AS posFROM next_pos, piece_after_movement, collision) movement,-- update the board considering the movementLATERAL (-- board with the new blocks, if the current piece reached the endWITH new_board(board) AS (SELECTCASE-- a new piece is going to spawn, meaning the previous piece blocks can be-- added to the boardWHEN movement.pos[4] = 2 THEN (-- last piece, to add to the boardWITH RECURSIVE last_piece(piece) AS (SELECT array_agg(cell)FROM (SELECT unnest(piece) + main.pos[3] AS cellFROM tetrominoWHERE id = main.pos[1]AND rotation = main.pos[2]) _),-- since the board is immutable, each piece block must be incrementally-- added to it, using a recursive queryboard_with_piece(i, board) AS (SELECT 1 AS i, main.boardUNION ALLSELECT board_with_piece.i + 1,CASE-- block in the boardWHEN piece[i] >= 0 THENboard_with_piece.board[:piece[i]] || '{t}'|| board_with_piece.board[piece[i] + 2:]-- block coordinates are not in the board, skip. can happen-- when a piece is rotated while at the topELSEboard_with_piece.boardENDFROM board_with_piece, last_pieceWHERE board_with_piece.i <= array_length(piece, 1))-- retrieve the last materialization of the boardSELECT boardFROM board_with_pieceORDER BY i DESCLIMIT 1)-- the piece did not reach the end yet, keep the same boardELSEmain.boardEND AS board),-- remove any completed lines from the new boardnew_board_compressed AS (-- aggregate back into a single array; count the number of remaining linesSELECT array_agg(cell ORDER BY line_number, col_number) AS board,(count(*) / (const.width + 1))::int AS num_linesFROM (-- filter out completed linesSELECT line_number, generate_series(0, const.width) AS col_number, unnest(line) AS cellFROM (-- split into one board line per rowSELECT i AS line_number, board[i*(const.width + 1)+1:(i+1)*(const.width+1)] lineFROM new_board, generate_series(0, const.height - 1) _(i)) _-- filter out lines that have only true valuesWHERE NOT line <@ ARRAY[true]) _)-- add new empty lines at the top of the board, if needed, and compute the number of-- lines cleared and points earnedSELECT string_to_array(repeat(repeat('f', const.width) || 't', const.height - num_lines), NULL)::bool[]|| board AS board,const.height - num_lines AS lines_cleared,(SELECT points *(greatest(1, (main.lines / const.lines_per_level + 1) * const.level_score_multiplier))FROM points_per_lineWHERE lines = const.height - num_lines) AS earned_pointsFROM new_board_compressed) next_board,-- find out how many lines can we drop the current pieceLATERAL (WITH RECURSIVE curr_piece(piece) AS (SELECT pieceFROM tetrominoWHERE id = movement.pos[1]AND rotation = movement.pos[2]),-- move the piece line by line until it collides with a block or reaches the end.-- if the piece cannot move a single line, return -1t (lines) AS (SELECT -1UNION ALLSELECT lines + 1FROM t, curr_pieceWHERE NOT (SELECT bool_or(cell) OR bool_or(cell IS NULL)FROM unnest(piece) p(coord)-- left join with the board to check the validity of the piece blocks-- (left and not inner since we also need to check piece blocks out of bounds)LEFT JOIN unnest(next_board.board) WITH ORDINALITY b(cell, ordinality)ON (p.coord + movement.pos[3]) + 1 + (lines + 1) * (const.width + 1) = b.ordinalityWHERE (p.coord + movement.pos[3]) + 1 + (lines + 1) * (const.width + 1) >= 1))SELECT max(lines) AS linesFROM t) drop_piece,-- generate the next piece (if necessary), using a similar algorithm to NES Tetris: first,-- a piece is randomly selected; if it is different from the previous one, it becomes the-- next piece; otherwise, we generate another random piece and use it as the next piece.-- this is biased to not select the same piece twice in a row, but can still happen (1/49)LATERAL (SELECTCASE-- next piece neededWHEN movement.pos[4] = 2 THEN (SELECT idFROM (-- first piece roll, discard it if it matches the previous pieceSELECT id, 0 AS rankFROM (SELECT idFROM tetromino-- the current frame is added to avoid the query from being cachedORDER BY random() + main.frameLIMIT 1) _WHERE id != movement.pos[1]UNION ALL-- second piece roll(SELECT id, 1 AS rankFROM tetromino-- the current frame is added to avoid the query from being cachedORDER BY random() + main.frameLIMIT 1)) _-- if we generated two valid pieces, select only the first oneORDER BY rankLIMIT 1)-- nothing to doELSEmain.next_pieceEND AS id) next_piece,-- compute the string to renderLATERAL (SELECT-- headerE'\n\n' ||(CASE WHEN input.cmd = 'p' THEN 'PAUSED' ELSE '' END) ||E'\nScore: ' || (main.score + next_board.earned_points) ||' / Lines: ' || (main.lines + next_board.lines_cleared) ||' / Level: ' || ((main.lines + next_board.lines_cleared) / const.lines_per_level + 1) ||-- next piece indicatorE'\nNext: ' || (WITH RECURSIVE next_piece(piece) AS (SELECT array_agg(cell)FROM (SELECT unnest(piece) - 3 AS cellFROM tetrominoWHERE tetromino.id = next_piece.idAND tetromino.rotation = 0) _),next_piece_block(i, block) AS (SELECT 1 AS i, string_to_array(repeat(repeat('f', const.width) || E'\n', 2), NULL) AS blockUNION ALLSELECT i + 1, block[:piece[i]] || '{t}' || block[piece[i] + 2:]FROM next_piece_block, next_pieceWHERE i <= array_length(piece, 1))-- pretty print the next piece blocks, add extra spacing to align with the-- 'Next:' label, and remove the extra newlineSELECT replace(replace(replace(array_to_string(block[:array_length(block, 1) - 1], ''),'t', '[]'), 'f', ' '), E'\n', E'\n ')FROM next_piece_blockORDER BY i DESCLIMIT 1) ||-- boardE'\n+' || repeat('-', const.width * 2) || E'+\n' || (-- materialize the current piece and the ghost_piece, i.e., where the current-- piece is going to fall on the boardWITH RECURSIVE pieces(curr_piece, ghost_piece) AS (SELECT array_agg(curr_cell),array_agg(curr_cell + greatest(drop_piece.lines, 0) * (const.width + 1))FROM (SELECT unnest(piece) + movement.pos[3] AS curr_cellFROM tetrominoWHERE id = movement.pos[1]AND rotation = movement.pos[2]) _),-- materialize the board + ghost piece (ghost blocks marked with the '.' char)board_with_ghost_piece(i, board) AS (SELECT 1 AS i, next_board.board::char[]UNION ALLSELECT i + 1,CASEWHEN ghost_piece[i] >= 0 THENboard[:ghost_piece[i]] || '{.}' || board[ghost_piece[i] + 2:]ELSEboardEND::char[] AS boardFROM board_with_ghost_piece, piecesWHERE i <= array_length(curr_piece, 1)),-- materialize the (board + ghost piece) + current pieceboard_with_piece(i, board) AS (SELECT 1, boardFROM (SELECT boardFROM board_with_ghost_pieceORDER BY i DESCLIMIT 1) _UNION ALLSELECT i + 1,CASEWHEN curr_piece[i] >= 0 THENboard[:curr_piece[i]] || '{t}' || board[curr_piece[i] + 2:]ELSEboardEND::char[]FROM board_with_piece, piecesWHERE i <= array_length(curr_piece, 1)),-- add borders to the boardcomplete_board AS (SELECT (ordinality - 1) / (const.width + 1) AS line_number,ARRAY['|']::char[] ||(array_agg(cell ORDER BY ordinality))[:const.width] ||ARRAY['|', E'\n']::char[] AS lineFROM (SELECT *FROM unnest((SELECT boardFROM board_with_pieceORDER BY i DESCLIMIT 1)) WITH ORDINALITY AS _(cell, ordinality)) _GROUP BY 1)-- pretty print, converting 't' to '[]', '.' to '()', and 'f' to ' 'SELECT replace(replace(replace(array_to_string(array_agg(line ORDER BY line_number), ''),'t', '[]'), '.', '()'), 'f', ' ')FROM complete_board) || '+' || repeat('-', const.width * 2) || '+' AS string) render-- keep executing the main loop until the piece is not stuck at the start (-1)WHERE main.max_drop_lines >= 0)-- project only the maximum score at the endSELECT 'score: ' || max(score) AS game_overFROM main;
项目地址:https://github.com/nuno-faria/tetris-sql
这段代码还有一个特性,长时间运行会崩溃哈哈,磁盘占用会一直上涨。

2号知乎网友:Cv大法代码酱
我在数据圈混了十多年,跑过几百T的数仓,也给产品经理现场救过数据的命。你让我回想印象最深的SQL,不是语法有多花哨,而是它在真实场景里解决问题的那个瞬间——那种拍大腿的爽感,其他语言少见。
几年前帮一家电商做活动监控,运营跑来拍桌子,说必须在半小时内拉出过去30天,每个用户最近一次购买的商品和购买前的三个浏览记录。
说白了就是一个 session 内的“倒推三步”。当时 ETL 不可能重跑,数据在 Hive 里有几十亿行,正常的 join 写法直接超时。
最后我写了这么一条(简化版,脱敏了字段):
WITH user_events AS (SELECTuser_id,event_time,event_type,product_id,ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY event_time DESC) AS rnFROM app_eventsWHERE event_time >= date_sub(current_date, 30))SELECT a.user_id,a.event_time AS buy_time,a.product_id AS buy_product,b.product_id AS view_productFROM user_events aJOIN user_events bON a.user_id = b.user_idAND b.event_time < a.event_timeWHERE a.event_type = 'buy'AND b.rn BETWEEN a.rn + 1 AND a.rn + 3;
没用复杂的嵌套 join,也没 group 乱七八糟。要的就是窗口函数直接解决时序依赖问题,把它当成一个“可重置的计数器”,按用户分区排序。跑下来 Hive 上 20 分钟,运营的需求当场落地,活动也保住了。
这类 SQL 一旦习惯了窗口函数(ROW_NUMBER、LAG、LEAD、SUM OVER等),你会慢慢发现,它比你想象的更能取代很多中间表和存储过程。对 OLAP 型业务来说,窗口函数是必须吃透的武器。
还有次做实时监控,老板习惯问“现在注册进来的人,有多少到了支付这一步”。如果你在 OLTP 表上一顿 join,延迟能到几分钟。后来我们用 ClickHouse 写了这样的单表漏斗:
SELECTcountIf(step = 1) AS signup,countIf(step >= 2) AS click,countIf(step >= 3) AS payFROM (SELECTuser_id,maxIf(1, action = 'signup') AS step1,maxIf(2, action = 'click') AS step2,maxIf(3, action = 'pay') AS step3,greatest(step1, step2, step3) AS stepFROM eventsWHERE event_time > now() - INTERVAL 1 HOURGROUP BY user_id)
这条直接把漏斗计算压扁到一个 SQL,延迟常年在秒级,老板那边的大屏可以“滑动”刷新。业务方第一次意识到——实时漏斗不是需要堆一堆 ETL 批处理的,它在流式数据库里可以靠单条 SQL 实时做。
1)真正惊艳的 SQL,不一定是代码有多骚,而是解决了平常要绕几个系统才能搞定的事。
2)窗口函数是 OLAP 场景的顶配,聚合、排序、分组逻辑能一次性完成,少建中间表,少占存储。
3)选对数据库引擎很关键。同样的 SQL,Hive、ClickHouse、PostgreSQL、MySQL 执行计划和性能千差万别。
4)有时候让 SQL 少“说”两句话,去掉没必要的 join、聚合和子查询,反而能跑得更快。
3号知乎网友:杨嘻嘻
领导写的。当时刚毕业还比较青涩,后端会把N多个要检索的字段传入到存储过程中,我当时通常的写法是拼SQL,大概长这样:
DECLARE @WHERE VARCHAR(MAX) = '1=1';IF @TITLE <> ''BEGINSET @WHERE += ' AND Title = @TITLE'ENDIF @AGE <> ''BEGINSET @WHERE += ' AND Age = @Age'ENDEXEC('SELECT * FROM USER WHERE ' + @WHERE);
字段多的时候拼的SQL到后来我自己都弄不明白了。后来看了领导写的存储过程,大概长这样:
SELECT *FROM USERWHERE((@TITLE <> '' AND Title = @TITLE) OR @TITLE = '')AND((@Age <> '' AND Age = @Age) OR @Age = '')
执行效率比我写的不知道高了多少,只感叹又学了一招。
4号知乎网友:古月士心羊羽
一个经典的递归查询用于找出某个员工的所有下属,无论下属的级数有多深:
WITH RECURSIVE Subordinates AS (SELECT employee_id, manager_id, employee_nameFROM EmployeesWHERE manager_id IS NULL -- CEOUNION ALLSELECT E.employee_id, E.manager_id, E.employee_nameFROM Employees EINNER JOIN Subordinates S ON S.employee_id = E.manager_id)SELECT * FROM Subordinates;
如果字段的最大可能长度超过255字节,那么长度值可能…
只能说作者太用心了,优秀
感谢详解
一般干个7-8年(即30岁左右),能做到年入40w-50w;有…
230721