SQL 对查询?

sql*_*ery 4 query

我只有一张表,其中包含 3 列旅行公司。它显示了从 A 市到 B 市等地的公共汽车。我想知道这条路线被使用了多少次。我可以很容易地找到从 A 到 B 的一种方法,但我希望这个程序在同一行中从 B 到 A 自动求和。

在这种情况下,AB等同于BA。要求是为任意两个任意(和不同的)点和)获得 (( Xto Y) + ( Yto X))的 COUNT 。XY

示例表

ID | FROM | TO 
1  |  A   | B
2  |  C   | D
3  |  B   | A
4  |  C   | A
5  |  D   | C
Run Code Online (Sandbox Code Playgroud)

答案应该是

Route AB = 2
Route CD = 2
Route CA = 1
Run Code Online (Sandbox Code Playgroud)

等等。

对于任何想要帮助的人,这里是脚本形式的数据,便于复制/粘贴:

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;
Run Code Online (Sandbox Code Playgroud)

Vér*_*ace 7

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;
Run Code Online (Sandbox Code Playgroud)

结果(所有解决方案相同):

point_1, point_2, journey_count
      A        B              2
      A        C              1
      C        D              2
Run Code Online (Sandbox Code Playgroud)

小提琴在这里。这里的所有示例都使用 PostgreSQL 10,但任何主流 RDBMS 都应该可以工作(*)——也许需要做一些调整!

  • (*)
  • SQLite/SQL Server 没有LEAST()GREATEST()函数。
  • 小心某些系统的标识符情况
  • 对于某些服务器,小提琴可能有问题!

此处的下一个小提琴使用 PostgreSQL 10(对于 MySQL,CTE 需要版本 >= 8.0)。在 MySQL 上运行这个 fiddle 将提供额外的数据,因为CHECK CONSTRAINT我输入了,见下文。令人难以置信的是,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;
Run Code Online (Sandbox Code Playgroud)

这个子查询消除了CASE@stickybit 解决方案中重复语句的需要。

或者,CTE(通用表函数- 也可在 [此处] 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;
Run Code Online (Sandbox Code Playgroud)

最后一点(请原谅双关语!),您可能希望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
);
Run Code Online (Sandbox Code Playgroud)


sti*_*bit 3

您需要首先对起点和终点进行排序,以使具有相同终点的对相等。由于它只有两列,因此您可以在此处执行此操作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;
Run Code Online (Sandbox Code Playgroud)