这是一个例子。假设我有下表:
id | list
--------+----------
10 |
| 10,20
20 | 10,20
对于每个id
值,我想计算具有list
值的行数,其中包含id
值。结果将是这样的:
id | count of lists
--------+----------
10 | 2
| 0
20 | 2
我建议应该使用一个窗口函数,但似乎我无法从这样的函数中访问id
值。
我完全同意 BAD 设计。这个问题是关于可能性的。
任何MySQL / PostgreSQL解决方案都没问题。
答案 0 :(得分:0)
假设您正在使用MySQL,并假设您的表名为test
,并假设两列都是字符串类型:
SELECT
t1.id, count(t2.list)
FROM
(test t1 LEFT JOIN test t2 ON
(t2.list LIKE CONCAT('%,', t1.id, ',%')) OR
(t2.list LIKE CONCAT('%,', t1.id)) OR
(t2.list LIKE CONCAT(t1.id, ',%')))
GROUP BY t1.id;
请注意,此解决方案可能会非常慢,具体取决于您拥有的记录数量以及list
字段中字符串的平均长度。
如果你需要更快的东西,我认为它不可能只是一个查询。也许我们必须为此编写存储过程或某些应用程序逻辑,或者使用其他表或列以及一系列多个SQL语句。
答案 1 :(得分:0)
在开始之前,如上所述,这是一个糟糕的设计。但是,我会这样查询它:
CREATE TABLE #Lists (id int, list varchar(500));
INSERT INTO #Lists (id, list) VALUES
(10, NULL), (NULL, '10,20'), (20, '10,20');
WITH cte AS (
SELECT LEFT(list, INSTR(list, '%,%')-1) AS value, SUBSTRING(list, INSTR(list, '%,%') + 1, 500) AS list FROM #Lists
UNION ALL
SELECT CASE WHEN list LIKE ('%,%') THEN LEFT(list, INSTR(list, '%,%')-1) ELSE list END AS value, CASE WHEN list LIKE ('%,%') THEN SUBSTRING(list, INSTR(list, '%,%') + 1, 500) END AS list FROM cte
WHERE CHAR_LENGTH(list) > 0
)
SELECT value, COUNT(*) FROM cte GROUP BY value;
DROP TABLE #Lists;
此解决方案允许list
字符串中的任意数量的值(如'10,20,30')。
理想情况下,列表值应存储在单独的表中,以便每条记录都有一个值,例如CREATE TABLE BetterDesign (id int, value int) INSERT INTO BetterDesign (id, value) VALUES (10, NULL), (NULL, 10), (NULL, 20), (20, 10), (20, 20)
。除了其他一百万个原因外,查询SELECT value, COUNT(*) FROM BetterDesign GROUP BY value
会更好。话虽如此,我理解遗留系统的痛苦。