【问题标题】:Algorithm to do a summation over a column with random selection of data from other column从另一列随机选择数据对列进行求和的算法
【发布时间】:2015-12-28 19:27:51
【问题描述】:

我有一张这样的桌子:

CREATE TABLE Table1
    ([IdeaNr] int, [SubmitterName] varchar(4), [SubmitterDepartment] varchar(4))
;

INSERT INTO Table1
    ([IdeaNr], [SubmitterName], [SubmitterDepartment])
VALUES
    (1, 'Joe', 'Org1'),
    (1, 'Bill', 'Org2'),
    (1, 'Kate', 'Org1'),
    (1, 'Tom', 'Org3'),
    (2, 'Sue', 'Org2'),
    (3, 'Bill', 'Org2'),
    (3, 'Fred', 'Org1'),
    (4, 'Ted', 'Org3'),
    (4, 'Kate', 'Org1'),
    (4, 'Hank', 'Org3')
;

我想从查询中得到以下结果:

IdeaNr  SubmitterCount   SubmitterRndName   SubmitterRndDepartment
1       4                Joe or ...         Org1 (if Joe)
2       1                Sue                Org2
3       2                Bill or ...        Org2 (if Bill)
4       3                Ted or ...         Org3 (if Ted)

我已经尝试了很多关于 Table1 自身、派生表和 GROUP BY 的各种 JOIN 的事情,例如:

SELECT COUNT(IdeaNr) AS SubmitterCount,IdeaNr,SubmitterName,SubmitterDepartment
FROM Table1
GROUP BY IdeaNr,SubmitterName,SubmitterDepartment

我认为问题在于创建一种算法,该算法仅采用出现在一组 IdeaNr 中的第一个(或随机)名称和部门。绝对清楚的是,您可能会对此类数据产生误导性的解释,例如。 g.:

  • Org1 有 2 个想法
  • Org2 有 1 个想法
  • Org3 有 1 个想法

但是这种“错误的平均”对于任务来说是可以的。你能帮忙吗?

编辑:预期结果在讨论过程中不断演变。想要的结果改为:

IdeaNr  SubmitterCount   SubmitterRndName   SubmitterRndDepartment
1       4                Joe, Bill, ...     GroupIdea
2       1                Sue                Org2
3       2                Bill, Fred         GroupIdea
4       3                Ted, ...           GroupIdea

【问题讨论】:

  • 我更正了预期结果中的第三行,因为我认为您输入错误。如果我做错了,请进一步编辑。

标签: sql sql-server random sql-server-2012 group-by


【解决方案1】:

试试这样:

DECLARE @Table1 TABLE ([IdeaNr] int, [SubmitterName] varchar(4), [SubmitterDepartment] varchar(4));
INSERT INTO @Table1([IdeaNr], [SubmitterName], [SubmitterDepartment])
VALUES
    (1, 'Joe', 'Org1'),
    (1, 'Bill', 'Org2'),
    (1, 'Kate', 'Org1'),
    (1, 'Tom', 'Org3'),
    (2, 'Sue', 'Org2'),
    (3, 'Bill', 'Org2'),
    (3, 'Fred', 'Org1'),
    (4, 'Ted', 'Org3'),
    (4, 'Kate', 'Org1'),
    (4, 'Hank', 'Org3');

SELECT x.IdeaNr
      ,Count(x.IdeaNr)
      ,MAX(Submitter.SubmitterName) AS SubmitterRndName
      ,MAX(Submitter.SubmitterDepartment) AS SubmitterRndDepartment
FROM @Table1 AS x 
CROSS APPLY
(
    SELECT TOP 1 SubmitterName, SubmitterDepartment
    FROM @Table1 AS y 
    WHERE y.IdeaNr=x.IdeaNr
) AS Submitter
GROUP BY x.IdeaNr

还有一个想法,不知道你是否需要这个:

SELECT x.IdeaNr
      ,Count(x.IdeaNr)
      ,STUFF(
            (
            SELECT ', ' + y.SubmitterName --maybe with DISTINCT
            FROM @Table1 AS y
            WHERE y.IdeaNr=x.IdeaNr
            FOR XML PATH('')
            ),1,2,'') AS AllSubmitters
      ,STUFF(
            (
            SELECT ', ' + z.SubmitterDepartment --maybe with DISTINCT
            FROM @Table1 AS z
            WHERE z.IdeaNr=x.IdeaNr
            FOR XML PATH('')
            ),1,2,'') AS AllDepartments
FROM @Table1 AS x 
GROUP BY x.IdeaNr

这回来了

IdeaNr                  AllSubmitters              AllDepartments
1           4           Joe, Bill, Kate, Tom       Org1, Org2, Org1, Org3
2           1           Sue                        Org2
3           2           Bill, Fred                 Org2, Org1
4           3           Ted, Kate, Hank            Org3, Org1, Org3

编辑:按照您上次评论的想法:

SELECT x.IdeaNr
      ,COUNT(x.IdeaNr)
      ,STUFF(
            (
            SELECT DISTINCT ', ' + y.SubmitterName 
            FROM @Table1 AS y
            WHERE y.IdeaNr=x.IdeaNr
            FOR XML PATH('')
            ),1,2,'') AS AllSubmitters
      ,CASE WHEN COUNT(x.IdeaNr)=1 THEN (SELECT TOP 1 z.SubmitterDepartment FROM @Table1 AS z WHERE z.IdeaNr=x.IdeaNr)
            ELSE 'GroupIdea' END AS Departments
FROM @Table1 AS x 
GROUP BY x.IdeaNr

【讨论】:

  • 感谢您的回答!你的两个例子都很好。对于第一个程序:我以前从未听说过 CROSS APPLY。我在这里找到了一些解释,比较了 JOIN 和 CROSS APPLY:explainextended.com/2009/07/16/inner-join-vs-cross-apply
  • @Bebass,很高兴为您提供帮助。 CROSS APPLY 非常适合基于行的访问,尤其是与 XML 和函数连接。一个提示:您可以轻松地将第二种方法更改为Joe (Org1), Bill (Org2), Kate (Org1), Tom (Org3),依此类推。如果这是您需要的,请投票和/或标记为已接受,谢谢!
  • 第二个程序对我来说非常先进;)但是将多行数据连接成一个字符串是一个很好的技巧!但是这个想法让我想到了另一个想法:对于列 [AllDepartments],有一个条目“GroupIdea”就足够了,而不是所有部门名称。这将有助于在 Excel 中进行进一步评估,因为您在该列中只有一个元素,并且元素的重组并不多。
  • @Bebass,也就是说,如果只有一个,你想读部门的名字,如果有很多,你想读“GroupIdea”这个表达式?与提交者姓名相同?
  • 对于 [AllSubmitters] 列,您从第二个程序中获得的解决方案非常好(获取以逗号分隔的名称列表)。对于 [AllDepartments] 列,我想在只有一个时读取部门的名称,如果有很多(如您所写),则读取表达式“GroupIdea”。我会尽量让你的代码适应这个想法。
【解决方案2】:

如果您想了解有关此主题的更多信息,请搜索 top-N-per-group。在 SQL Server 中使用CROSS APPLY 很容易。

SQL Fiddle

WITH
CTE
AS
(
    SELECT
        IdeaNr
        ,COUNT(*) AS SubmitterCount
    FROM @Table1
    GROUP BY IdeaNr
)
SELECT
    CTE.IdeaNr
    ,CTE.SubmitterCount
    ,CA.SubmitterName
    ,CA.SubmitterDepartment
FROM
    CTE
    CROSS APPLY
    (
        SELECT TOP(1)
            T.SubmitterName
            ,T.SubmitterDepartment
        FROM @Table1 AS T
        WHERE T.IdeaNr = CTE.IdeaNr

        --ORDER BY T.SubmitterName
        --ORDER BY T.SubmitterDepartment
        --ORDER BY CRYPT_GEN_RANDOM(4)

    ) AS CA
ORDER BY CTE.IdeaNr;

如果您没有将任何ORDER BY 放入CROSS APPLY 部分,服务器将选择一个“随机”行。它本身不是随机的,但是当您多次运行此查询时,结果可能相同或可能不同。在实践中,如果您在表上创建或删除索引,结果很可能会有所不同,但如果表很大,则每次运行查询时它们可能会有所不同。

如果您想为每个 IdeaNr 选择特定的行,请使用 ORDER BY 名称或部门或一些 ID 等。

如果你想选择一个真正随机的行,那么ORDER BY CRYPT_GEN_RANDOM(4)

当我在没有任何索引的情况下使用表变量进行此测试时,我得到以下没有任何 ORDER BY 的结果:

IdeaNr    SubmitterCount    SubmitterName    SubmitterDepartment
1         4                 Joe              Org1
2         1                 Sue              Org2
3         2                 Bill             Org2
4         3                 Ted              Org3

看起来好像它为每个 IdeaNr 按添加到表中的顺序选择了“第一”行。但是,不要被愚弄了,没有明确的ORDER BY,订单是不能保证的。如果您想获取每个IdeaNr 的第一行,因为它们被添加到表中,您需要以某种方式存储有关此订单的信息。例如,将列ID int IDENTITY 添加到表中,该列会随着新行的添加而自动增加,然后您可以像这样使用它ORDER BY ID DESC 来获得有保证的结果。

使用 SQL Fiddle 看看它是如何工作的。

【讨论】:

  • 非常感谢您的回答。我经常使用 SQLite,所以直到现在我一直在避免 CTE。但在我看来,这是一个非常有用的概念。我在这里找到了一些介绍:simple-talk.com/sql/t-sql-programming/sql-server-cte-basics 甚至 SQLite 现在似乎也有了:sqlite.org/lang_with.html
  • 我也喜欢你对 ORDER BY CRYPT_GEN_RANDOM(4) 的建议!
  • @Bebass,CTE 使查询更具可读性。在这种情况下,可以将 SELECT ... GROUP BY 作为嵌套查询放入主查询。但是,看起来您的问题已经发生了变化,并且您正在寻求不同的结果,因此这个答案变得不那么相关了。
  • 是的,问题已经演变。我对问题的变化发表了评论。不过,非常感谢您的详细回答 - 这将鼓励我更多地了解 CTE。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-01-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-28
  • 2018-05-22
  • 2021-11-21
相关资源
最近更新 更多