SQL 對查詢?
我只有一張表,其中包含旅遊公司的 3 列。它顯示了從城市 A 到城市 B 等的公共汽車。我想知道這條路線使用了多少次。我可以很容易地找到從 A 到 B 的一種方式,但我希望這個程序在同一行中自動從 B 到 A 求和。
在這種情況下,
A
toB
等同於B
toA
。要求是獲得任意兩個任意(和不同)點和) 的 ((X
toY
) + (Y
to )) 的 COUNT 。X``X``Y
範例表
ID | FROM | TO 1 | A | B 2 | C | D 3 | B | A 4 | C | A 5 | D | C
答案應該是
Route AB = 2 Route CD = 2 Route CA = 1
等等
對於任何想要幫助的人,這裡是腳本形式的數據,以便於複製/粘貼:
CREATE TABLE Routes ( ID INT NOT NULL, ORIGIN VARCHAR(2) NOT NULL, DESTINATION VARCHAR(2) NOT NULL ); INSERT INTO Routes ( ID, ORIGIN, DESTINATION ) VALUES ( 1, 'A', 'B' ), ( 2, 'C', 'D' ), ( 3, 'B', 'A' ), ( 4, 'C', 'A' ), ( 5, 'D', 'C' ) ; SELECT ID, ORIGIN, DESTINATION FROM Routes; DROP TABLE Routes;
3 種解決方案(其中 2 種與 @stickybit 的類似,但更易於使用)如下。
我經常發現查看對問題有多種解決方案的答案/執行緒是有益的——其中一些顯然比其他的更好,但它可以成為一種學習體驗!
最簡單也是迄今為止最優雅的解決方案是(感謝@ypercube(tm) 的提示)是:
SELECT LEAST(origin, destination) AS point_1, GREATEST(origin, destination) AS point_2, COUNT(*) AS journey_count FROM route GROUP BY point_1, point_2 ORDER BY point_1, point_2;
結果(所有解決方案都相同):
point_1, point_2, journey_count A B 2 A C 1 C D 2
小提琴就在這裡。這裡的所有範例都使用 PostgreSQL 10,但任何主流 RDBMS 都應該可以工作(*)——也許需要一些調整!
- (*)
- SQLite/SQL Server 沒有
LEAST()
orGREATEST()
函式。- 小心某些系統的標識符情況
- 對於某些伺服器來說,小提琴可能是錯誤的!
此處的下一個小提琴使用 PostgreSQL 10(對於 MySQL,CTE 需要版本 >= 8.0)。由於
CHECK CONSTRAINT
我輸入了,在 MySQL 上執行這個小提琴會提供額外的數據,見下文。令人難以置信的是,MySQL仍然沒有它們!MariaDB 確實實現了CHECK
s.SELECT point_1, point_2, count(*) FROM ( SELECT CASE WHEN origin < destination THEN origin ELSE destination END AS point_1, CASE WHEN destination > origin THEN destination ELSE origin END as point_2 FROM routes ) AS tab GROUP BY point_1, point_2 ORDER BY point_1, point_2;
這個子查詢消除了
CASE
@stickybit 解決方案中重複語句的需要。或者,CTE(通用表功能- 也可用
$$ here $$https://dbfiddle.uk/?rdbms=postgres_10&fiddle=734ef45d84f5fb9cbba84cd1714318df )) 可以用於相同的目的。對於更長、更複雜的查詢,這可能是要走的路——CTE 是天賜之物!
WITH the_route AS ( SELECT CASE WHEN origin < destination THEN origin ELSE destination END AS point_1, CASE WHEN destination > origin THEN destination ELSE origin END as point_2 FROM routes ) SELECT point_1, point_2, COUNT(*) FROM the_route GROUP BY point_1, point_2 ORDER BY point_1, point_2;
最後一點(請原諒雙關語!),您可能希望
CHECK CONSTRAINT
通過確保來源和目的地永遠不會與以下相同來將 a 添加到您的表定義中:CREATE TABLE Routes ( route_id INTEGER NOT NULL, origin VARCHAR(2) NOT NULL, destination VARCHAR(2) NOT NULL, -- CHECK (destination != origin) - can do it this way (remove -- comment) CONSTRAINT routes_orig_dest_distinct_ck CHECK (destination != origin) -- Better as it gives a meaningful name to the CONSTRAINT -- You can check this by swapping the CONSTRAINTs );
您需要首先對起點和終點進行排序,以使具有相同端點的對相等。由於它只有兩列,您可以在
CASE ... END
此處執行此操作。然後你可以GROUP BY
得到這些並得到count(*)
.SELECT CASE WHEN origin <= destination THEN origin ELSE destination END, CASE WHEN destination >= origin THEN destination ELSE origin END, count(*) FROM routes GROUP BY CASE WHEN origin <= destination THEN origin ELSE destination END, CASE WHEN destination >= origin THEN destination ELSE origin END;