我只有一张表,其中包含 3 列旅行公司。它显示了从 A 市到 B 市等地的公共汽车。我想知道这条路线被使用了多少次。我可以很容易地找到从 A 到 B 的一种方法,但我希望这个程序在同一行中从 B 到 A 自动求和。
在这种情况下,A
以B
等同于B
对A
。要求是为任意两个任意(和不同的)点和)获得 (( X
to Y
) + ( Y
to X
))的 COUNT 。X
Y
示例表
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)
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 都应该可以工作(*)——也许需要做一些调整!
LEAST()
或GREATEST()
函数。此处的下一个小提琴使用 PostgreSQL 10(对于 MySQL,CTE 需要版本 >= 8.0)。在 MySQL 上运行这个 fiddle 将提供额外的数据,因为CHECK CONSTRAINT
我输入了,见下文。令人难以置信的是,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;
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)
您需要首先对起点和终点进行排序,以使具有相同终点的对相等。由于它只有两列,因此您可以在此处执行此操作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)