Query

SQL 對查詢?

  • October 30, 2018

我只有一張表,其中包含旅遊公司的 3 列。它顯示了從城市 A 到城市 B 等的公共汽車。我想知道這條路線使用了多少次。我可以很容易地找到從 A 到 B 的一種方式,但我希望這個程序在同一行中自動從 B 到 A 求和。

在這種情況下,AtoB等同於Bto A。要求是獲得任意兩個任意(和不同)點和) 的 (( Xto Y) + ( Yto )) 的 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 確實實現了CHECKs.

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;

引用自:https://dba.stackexchange.com/questions/208633