请问您见过最惊艳的sql查询语句是什么?
可能是这个吧,用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
这段代码还有一个特性,长时间运行会崩溃哈哈,磁盘占用会一直上涨。
select * from girls where age between 20 and 30 and boyfriend is null order by cup desc
一个经典的递归查询用于找出某个员工的所有下属,无论下属的级数有多深:
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;
GitHub上有很多有意思的sql查询语句和项目。
1、SQL Murder Mystery: 一个有趣的交互式游戏,通过解决 SQL 查询谜题来解决一个虚构的凶杀案。
2、SQL Style Guide: 一个关于 SQL 编码风格和最佳实践的指南,其中包含一些有趣的示例和案例研究。
3、SQL-Interview-Preparation: 这个存储库收集了一些常见的 SQL 面试问题和解答,可以帮助你准备 SQL 面试。
4、SQL Advent Calendar: 这个存储库收集了每年 12 月期间发布的有趣的 SQL 相关的博客文章和示例代码,涵盖了各种主题。
5、SQL Queries for Data Analysis: 一个收集了用于数据分析的 SQL 查询示例的存储库,包含了从简单到复杂的查询示例。
还有一些展示了高级和复杂SQL技巧,可以让我们在查询和处理方面更加灵活的语句。
使用自连接查询找出同一表中具有相同值的记录:
SELECT A.column_name, B.column_nameFROM table_name A, table_name BWHERE A.column_name = B.column_nameAND A.id <> B.id;
SELECT name, age,CASEWHEN age < 18 THEN '未成年'WHEN age >= 18 AND age < 65 THEN '成年'ELSE '老年'END AS age_groupFROM customers;
SELECT date, revenue,SUM(revenue) OVER (ORDER BY date) AS cumulative_sumFROM sales;
SELECT A.column_name, B.column_nameFROM table_A ACROSS JOIN table_B B;
WITH RECURSIVE tree_path AS (SELECT id, name, CAST(name AS VARCHAR(255)) AS pathFROM categoriesWHERE parent_id IS NULLUNION ALLSELECT c.id, c.name, CONCAT(tp.path, ' > ', c.name)FROM categories cINNER JOIN tree_path tp ON c.parent_id = tp.id)SELECT id, name, pathFROM tree_path;
SQL语句如何优化?
要遵循一些最佳实践,一张图就可以知道高质量SQL是怎么写的:
我们根据图片总结一下所有需要注意的事项:
保持简洁:
编写简洁明了的SQL语句。
避免使用过于复杂的逻辑。
尽量减少嵌套子查询,使用连接(JOIN)或窗口函数等方法简化查询。
使用正确的数据类型:
选择与数据内容匹配的数据类型。
根据数据特性选择合适的数据类型,如整数、小数、日期和时间、字符串和布尔值。
格式化:
使用一致的缩进、空格和换行。
保持代码整洁、易读。
为表和列取有意义的名称:
使用描述性的表和列名。
遵循团队或组织的命名规范。
使用别名:
为表和列使用简短的别名,使代码更简洁。
别名应该简洁且具有描述性。
注释:
保持注释简洁明了。
解释代码的目的和功能。
使用标准的注释格式。
更新注释以反映代码的变化。
避免使用 SELECT *:
明确列出所需查询的列名。
减少不必要的数据传输和性能损失。
优化查询性能:
关注潜在的性能问题,如全表扫描、缺乏索引等。
合理地使用索引,以提高查询性能。
使用事务:
在处理多个相关操作时,使用事务来保证数据的一致性。
注意事务的隔离级别和锁定策略。
测试和审查:
为SQL语句编写测试用例。
验证查询结果的正确性和性能。
邀请同事或团队成员审查代码。
关注可读性、性能和功能正确性。
原文:https://www.zhihu.com/question/384673958
- END -
IT168与ITPUB技术社区强强联手,收集数百款主流数据库产品,重磅推出“数据库全景图”,旨在打造一款集知识普及、产品对比、选型参考于一体的综合性资源平台。“数据库全景图(11月版)”可扫描上方左侧二维码回复关键词获取,识别右侧二维码直达“数据库全景图”链接(右上角浏览器打开获取更好体验)。