SQL Server count() over() with distinct
Posted
技术标签:
【中文标题】SQL Server count() over() with distinct【英文标题】:SQLServer count() over() with distinct 【发布时间】:2016-01-14 21:41:28 【问题描述】:我正在做一个项目,我们需要计算不同行的数量。该场景的简化版本包括user
表、keyword
表和keyword_user
表。
user
表只包含常见的用户元数据,如姓名等。其他表如下所列。
keyword_user:
id
user_id
keyword_id
关键词:
id,
description
我想要做的是根据用户关键字 ID 找到最大用户数 (5),同时还要计算匹配行的总数。计数必须是不同的。
查询:
SELECT TOP 5 u.[id],
u.[firstname],
u.[lastname],
total = Count(*) OVER()
FROM [user] u
INNER JOIN [keyword_user] ku
ON u.[id] = ku.[user_id]
WHERE ( ku.keyword_id IN ( '5f6501ec-0a71-4067-a21d-3c5f87a76411', 'c19b95c0-8554-4bbd-9526-db8f1c4f1edf'))
AND u.id NOT IN ( '12db3001-b3b9-4626-8a02-2519102cb53a' )
结果集:
+--------------------------------------+-----------+----------+-------+
| id | firstname | lastname | total |
+--------------------------------------+-----------+----------+-------+
| F0527AC3-747A-45A6-9CF9-B1F6C7F548F8 | Kasper | Thomsen | 3 |
| 95988F6D-9C91-4779-B6C3-3D4B4D6AE836 | Michael | Jacobsen | 3 |
| 95988F6D-9C91-4779-B6C3-3D4B4D6AE836 | Michael | Jacobsen | 3 |
+--------------------------------------+-----------+----------+-------+
问题:
这里的问题是,Michael 被计算了两次,因此总计数为 3,而我希望它为 2。使用 count() over()
时,您无法将包含不同的表达式解析到其中。另外,如果我只是SELECT DISTINCT
,我的结果集看起来很好,除了总数,它仍然是 3。
如果我需要提供更多信息来支持这个问题,请告诉我,我会尽力回答。
MSSQL 创建数据库脚本(样本数据)
example_data.sql
想要的结果集:
+--------------------------------------+-----------+----------+-------+
| id | firstname | lastname | total |
+--------------------------------------+-----------+----------+-------+
| F0527AC3-747A-45A6-9CF9-B1F6C7F548F8 | Kasper | Thomsen | 2 |
| 95988F6D-9C91-4779-B6C3-3D4B4D6AE836 | Michael | Jacobsen | 2 |
+--------------------------------------+-----------+----------+-------+
【问题讨论】:
亲爱的,我删除了我的答案,我认为一个解决方案 试试我的更新答案 是的,给我一秒钟,我会试试看 :) 另外请注意我添加了一个带有示例数据的脚本 @ThomasTeilmann,请使用架构、示例数据和预期结果创建 SqlFiddle,而不是发布指向 Dropbox 的链接。目前,您在问题中发布的预期结果集与示例数据不匹配。 【参考方案1】:试试这个:
我创建了两个临时表(#user
和 #user_key
)并填充它们。
create table #user (id int, name varchar(20))
create table #user_key (id int, fk_user int, content varchar(50))
insert into #user values
(1, 'Giuseppe'),
(2, 'Anna'),
(3, 'Angela'),
(4, 'Maria'),
(5, 'Ethra'),
(6, 'Piero')
insert into #user_key values
(1, 1, 'ciao'),
(2, 1, 'hello'),
(3, 2, 'hallo'),
(4, 4, 'hullo')
提取查询:
我将#user
用作主表,因此我添加了一个关于总数的子查询,但在 order by 子句中,我尝试对用户的关键字进行排序。您可以添加其他条件(如您的 In / NOT IN)
select top 5 id, name, (select COUNT(*) from #user_key uk)
from #user u
order by (select COUNT(*) from #user_key uk where uk.fk_user = u.id) desc
转到SqlFiddle
编辑
你想要这个?:
97D476C2-B52C-4D44-A460-44472CBF8817 Michael testing 2
F4FE5550-BC69-437E-91A0-5B11E0D9279E Kasper Test 2
还是这个?
97D476C2-B52C-4D44-A460-44472CBF8817 Michael testing 2
F4FE5550-BC69-437E-91A0-5B11E0D9279E Kasper Test 2
12DB3001-B3B9-4626-8A02-2519102CB53A Thomas Teil 2
【讨论】:
不幸的是,这不会按预期工作。计数将显示用户被选中的次数,即单个用户匹配了多少关键字。示例:如果 michael 在 Where 子句中包含两个关键字,则该行的计数将为 2。我需要与这两个关键字匹配的所有用户的总数,但只选择其中的 5 个。例如,计数器可以是 77。 对不起,但我很难将您建议的答案与我陈述的场景联系起来:/ 看来您并不完全理解我面临的问题。真的很难解释。你能看看我为 Ken Lacostes 的答案写的 cmets 吗?【参考方案2】:你可以试试:
SELECT TOP 5 * FROM (
SELECT
u.[id],
u.[firstname],
u.[lastname],
total = Count(*) OVER(PARTITION BY ku.keyword_id),
rownum = ROW_NUMBER() OVER(PARTITION BY ku.keyword_id ORDER BY u.ID)
FROM [user] u
INNER JOIN [keyword_user] ku
ON u.[id] = ku.[user_id]
WHERE (ku.keyword_id IN ( '5f6501ec-0a71-4067-a21d-3c5f87a76411', 'c19b95c0-8554-4bbd-9526-db8f1c4f1edf'))
AND u.id NOT IN ( '12db3001-b3b9-4626-8a02-2519102cb53a' )
) AS A ORDER BY A.rownum DESC
【讨论】:
如果我将该表达式放在 over 子句中,它只会向上计数一个 pr。与 where 子句中的关键字匹配的关键字。 IE。 Michaels 计数为 2,Kaspers 计数为 1。这里的想法是获取可能匹配这些给定关键字的用户总数,但只选择最大值。其中 5 个,同时仍然得到总数。 :) 你能给我样本数据的脚本吗? 是的,我可以,请给我几分钟时间来创建它。 在那里,我在帖子底部链接到一个 db 脚本。 :) 我想选择 5 个用户,他们在 where 子句中包含这些关键字中的任何一个。每个用户必须有一个计数,以反映数据库中有多少用户与这些关键字匹配,同时仍返回 5 行。示例:可能有 77 个用户选择了我们在搜索中包含的相同关键字。因此,我们仍然应该获得最多 5 个用户,而计数反映了有多少用户拥有此关键字。 (77)。请阅读我为 Ken Lacostes 写的 cmets 答案:)【参考方案3】:在您的情况下,我有点困惑,特别是“关键字”以及它们如何与每个用户相关(这对我来说只是一个流程问题)因此发现自己通过包含您的初始查询作为我的源表。
请在下方发表评论,以便我们改进。
SELECT
id
, firstname
, lastname
, total
, COUNT(*) AS [per_user_count]
FROM (
SELECT TOP 5 u.[id],
u.[firstname],
u.[lastname],
total = Count(*) OVER()
FROM [user] u
INNER JOIN [keyword_user] ku
ON u.[id] = ku.[user_id]
WHERE
(
ku.keyword_id IN (
'5f6501ec-0a71-4067-a21d-3c5f87a76411'
, 'c19b95c0-8554-4bbd-9526-db8f1c4f1edf'
)
)
AND u.id NOT IN ('12db3001-b3b9-4626-8a02-2519102cb53a')
) AS T
GROUP BY
T.id
, T.firstname
, T.lastname
, T.total
编辑:我们真的很困惑,所以我创建了一个更简单的脚本,该脚本将排除关键字,仅排除唯一用户(生成总体总数)并获得其中的前 5 个(随机顺序)。
SELECT
TOP 5
T.id
, T.firstname
, T.lastname
, Total = COUNT(*) OVER()
FROM (
SELECT DISTINCT
u.*
FROM [keyword_user] ku
LEFT JOIN [user] u
ON
ku.user_id = u.id
WHERE
(
ku.keyword_id IN (
'5f6501ec-0a71-4067-a21d-3c5f87a76411'
, 'c19b95c0-8554-4bbd-9526-db8f1c4f1edf')
)
AND ku.[user_id] NOT IN (
'12db3001-b3b9-4626-8a02-2519102cb53a'
)
) AS T
谢谢
编辑:您的方案是与实体相关联的直接“关键字搜索”,具有总计数和前 5 个结果。正如我对 CTE 的理解(并且基于 MSDN),CTE 是分层数据挖掘的一个很好的解决方案(不需要做 while 和做任何后空翻来获得你的组织层次结构),它并不真正适合场景我们在这里。
【讨论】:
是的,我明白你为什么对此感到困惑,因为最初的数据库设计相当大。这就是为什么我尝试制作它的简化版本:) 我会尝试你的建议。给我一分钟:) 啊,你在每一行都包含了一个计数,显示了用户被选中的次数,代表他有多少关键字。不幸的是,这并不是我真正感兴趣的。我想要一个反映数据库中有多少用户具有这些关键字的计数,但最多只返回 5 个。我知道它有点难以理解,但也很难解释:) 所以这不是个人计数,而是一种全球计数。它必须是不同的。 其实我只对总数感兴趣,但还是不明显。 (它在所有行上返回 3,在这种情况下它应该返回 2。)如果您想要挑战,我已经添加了一个带有一些示例数据的 db create 脚本 :) 每个用户有不同的关键字?还是不同的 USER PER 关键字? 两者都不是 :) 我知道这有点奇怪,但我想要的是一个数字,不知何故,数据库中有多少用户(总数)具有任何这些关键字。它可能是 77,同时仍返回 2 行。这两行的总数仍为 77。如果需要,请提出更多问题。我会尽力回答:)【参考方案4】:你真的应该在问题中解释你需要什么,而不是在 cmets 中。
在CTE_Users
中,我们找到给定关键字的所有不同用户。
然后用user
加入结果以获取用户详细信息。对于给定的小样本数据,至少它会产生您期望的结果。
WITH
CTE_Users
AS
(
SELECT DISTINCT ku.user_id
FROM
keyword_user AS ku
WHERE
ku.keyword_id IN (
'5f6501ec-0a71-4067-a21d-3c5f87a76411',
'c19b95c0-8554-4bbd-9526-db8f1c4f1edf')
AND ku.user_id NOT IN (
'12db3001-b3b9-4626-8a02-2519102cb53a')
)
SELECT TOP(5)
u.id
,u.firstname
,u.lastname
,COUNT(*) OVER() AS total
FROM
user AS u
INNER JOIN CTE_Users ON CTE_Users.user_id = u.id
;
【讨论】:
我要试试这个,但为了记录,我想我在问题中说得很清楚:“我想做的是找到最大用户数 (5),基于在用户的keyword_id上,同时还计算匹配行的总数。计数必须是不同的。”。您的答案似乎不错,但我无法在一小时左右之前对其进行测试。谢谢 这就是问题所在。你认为你说得很清楚,但只有在我阅读了你对 Nguyễn 的评论以及 77 个用户的例子后,我才开始理解你在说什么。您应该在问题中包含这些解释。 我有一个问题:如果我想加入更多表以进一步过滤用户,我应该把它放在哪里以获得最佳实践? (我需要加入用户ID(u.id) Err...... 这取决于......CTE_Users
应该包含不同用户 ID 的最终过滤列表,因此在此处添加额外过滤是有意义的。
谢谢,您的答案是最适合我的答案,我已接受它作为答案。干得好!以上是关于SQL Server count() over() with distinct的主要内容,如果未能解决你的问题,请参考以下文章
count(distinct) over (partition by... 在 Oracle SQL 中不起作用
怎样将sqlserver2005数据库中一张表的一半数据取出来
DB2 SQL Count over Union of multiple tables with different Datatype