代码之家  ›  专栏  ›  技术社区  ›  Ian Boyd

sql server 2000:执行连接聚合子查询的思想

  •  7
  • Ian Boyd  · 技术社区  · 15 年前

    我有一个返回我想要的行的查询,例如。

    QuestionID  QuestionTitle  UpVotes  DownVotes  
    ==========  =============  =======  =========  
    2142075     Win32: Cre...        0          0  
    2232727     Win32: How...        2          0  
    1870139     Wondows Ae...       12          0  
    

    现在我想要一个 返回,其中包含一个逗号分隔的列表“ 作者 “(例如原始海报和编辑)。例如。:

    QuestionID  QuestionTitle  UpVotes  DownVotes  Authors
    ==========  =============  =======  =========  ==========
    2142075     Win32: Cre...        0          0  Ian Boyd  
    2232727     Win32: How...        2          0  Ian Boyd, roygbiv
    1870139     Wondows Ae...       12          0  Ian Boyd, Aaron Klotz, Jason Diller, danbystrom
    


    假亦真

    SQL Server 2000没有 CONCAT(AuthorName, ', ') 聚合操作,我一直在伪造它-为 TOP 1 作者,作者计数。

    QuestionID  QuestionTitle  UpVotes  DownVotes  FirstAuthor  AuthorCount  
    ==========  =============  =======  =========  ===========  =========== 
    2142075     Win32: Cre...        0          0  Ian Boyd               1 
    2232727     Win32: How...        2          0  Ian Boyd               2
    1870139     Wondows Ae...       12          0  Ian Boyd               3
    

    如果有多个作者,那么我将向用户显示一个省略号(“a”),表示有多个作者。例如,用户将看到:

    QuestionID  QuestionTitle  UpVotes  DownVotes  Authors
    ==========  =============  =======  =========  ==========
    2142075     Win32: Cre...        0          0  Ian Boyd
    2232727     Win32: How...        2          0  Ian Boyd, …
    1870139     Wondows Ae...       12          0  Ian Boyd, …
    

    这很有效,因为通常 问题 没有被编辑-这意味着我完全支持99%的情况,而1%的情况只有一半 阿西德 也。


    线程重新查询

    作为一个更复杂、更容易出错的解决方案,我正在考虑迭代显示的列表,并为每个列表创建一个线程池工作线程。” 问题 “在列表中,对数据库执行查询以获取作者列表,然后在内存中聚合该列表。这意味着列表将首先填充(本机)应用程序。之后我会发布几千个单独的查询。

    但那将是可怕的,可怕的,可怕的,缓慢的。更不用说虫洞了,因为它是线程工作。


    是的是的

    亚当机械师 says quite plainly :

    不要将行连接成分隔符 SQL Server中的字符串。做IT客户端 一边。

    告诉我怎么做,我就做。


    有谁能想出比我原来的“前1个加上椭圆”的解决方案更快(比如说…在一个数量级内)的更好的解决方案吗?

    例如,是否有方法返回结果集,其中reach行具有关联的结果集?所以对于每个“master”行,我可以得到包含列表的“detail”结果集。


    最佳答案代码

    Cade's 链接到 Adam Machanic's solution 我最喜欢。一个用户定义的函数,似乎是通过魔术来操作的:

    CREATE FUNCTION dbo.ConcatAuthors(@QuestionID int)
    RETURNS VARCHAR(8000)
    AS
    BEGIN
        DECLARE @Output VARCHAR(8000)
        SET @Output = ''
    
        SELECT @Output =    CASE @Output 
                    WHEN '' THEN AuthorName 
                    ELSE @Output + ', ' + AuthorName 
                    END
        FROM  (
            SELECT QuestionID, AuthorName, QuestionDate AS AuthorDate FROM Questions
            UNION
            SELECT QuestionID, EditorName, EditDate FROM QuestionEdits
        ) dt
        WHERE dt.QuestionID = @QuestionID
        ORDER BY AuthorDate
    
        RETURN @Output
    END
    

    t-sql用法为:

    SELECT QuestionID, QuestionTitle, UpVotes, DownVotes, dbo.ConcatAuthors(AuthorID)
    FROM Questions
    
    4 回复  |  直到 13 年前
        1
  •  3
  •   Community CDub    6 年前

    看看这些文章:

    http://dataeducation.com/rowset-string-concatenation-which-method-is-best/

    http://www.simple-talk.com/sql/t-sql-programming/concatenating-row-values-in-transact-sql/ (请参阅响应中的Phil Factor交叉连接解决方案-它将在SQL Server 2000中工作)

    显然,在sql server 2005中,for xml技巧是最简单、最灵活、通常也是最有效的。

    至于为每一行返回一个行集,如果出于某种原因仍要这样做,则可以在存储过程中这样做,但客户端需要使用第一个行集中的所有行,然后转到下一个行集并将其与第一个行集中的第一行相关联,等等。将需要打开它作为第一个行集返回的同一个集上的游标,并按顺序运行多个选择以生成所有子行集。这是我的一种技巧,但只有在 所有 实际需要的数据(例如,在完全填充的树视图中)。

    不管人们怎么说,在客户端做这件事通常是对带宽的极大浪费,因为返回所有行并在客户端进行循环和中断意味着在每一行的开头传输大量相同的列只是为了得到不断变化的列在行的末尾。

    无论你做什么,都应该是 知情决定 基于 你的 用例。

        2
  •  1
  •   Mark Starmer    13 年前

    我尝试了三种方法来解决这个问题,一种贴在这里的方法,activex脚本和udf函数。

    对我来说,最有效的脚本(速度方面)是运行多个查询以获取额外数据的奇异axtive-x脚本。

    udf平均花了22分钟来转换,subquery方法(贴在这里)花了大约5米,activex脚本花了4米30,这让我非常恼火,因为这是我希望丢弃的脚本。我得看看能不能在别的地方提高效率。

    我认为额外的30s被tempdb用来存储数据,因为我的脚本需要一个order by。

    值得注意的是,我正在收集大量的文本数据。

        3
  •  0
  •   MicSim    15 年前

    你也可以看看这个 script . 这基本上就是cade roux在他的文章中提到的交叉连接方法。

    上面的方法看起来非常干净:您必须首先创建一个视图,然后根据视图中的值创建一个语句。您可以在代码中动态构建第二个sql语句,因此应该直接使用它。

        4
  •  0
  •   KM.    15 年前

    我不确定这是否适用于SQL Server 2000,但您可以尝试:

    --combine parent and child, children are CSV onto parent row
    CREATE TABLE #TableA (RowID int, Value1 varchar(5), Value2 varchar(5))
    INSERT INTO #TableA VALUES (1,'aaaaa','A')
    INSERT INTO #TableA VALUES (2,'bbbbb','B')
    INSERT INTO #TableA VALUES (3,'ccccc','C')
    
    CREATE TABLE #TableB (RowID int, TypeOf varchar(10))
    INSERT INTO #TableB VALUES (1,'wood')
    INSERT INTO #TableB VALUES (2,'wood')
    INSERT INTO #TableB VALUES (2,'steel')
    INSERT INTO #TableB VALUES (2,'rock')
    INSERT INTO #TableB VALUES (3,'plastic')
    INSERT INTO #TableB VALUES (3,'paper')
    
    
    SELECT
        a.*,dt.CombinedValue
        FROM #TableA        a
            LEFT OUTER JOIN (SELECT
                                 c1.RowID
                                     ,STUFF(
                                              (SELECT
                                                   ', ' + TypeOf
                                                   FROM (SELECT
                                                             a.RowID,a.Value1,a.Value2,b.TypeOf
                                                             FROM #TableA                 a
                                                                 LEFT OUTER JOIN #TableB  b ON a.RowID=b.RowID
                                                        ) c2
                                                   WHERE c2.rowid=c1.rowid
                                                   ORDER BY c1.RowID, TypeOf
                                                   FOR XML PATH('') 
                                              )
                                              ,1,2, ''
                                           ) AS CombinedValue
                                 FROM (SELECT
                                           a.RowID,a.Value1,a.Value2,b.TypeOf
                                           FROM #TableA                 a
                                               LEFT OUTER JOIN #TableB  b ON a.RowID=b.RowID
                                      ) c1
                                 GROUP BY RowID
                            ) dt ON a.RowID=dt.RowID
    

    来自SQL Server 2005的输出:

    RowID       Value1 Value2 CombinedValue
    ----------- ------ ------ ------------------
    1           aaaaa  A      wood
    2           bbbbb  B      rock, steel, wood
    3           ccccc  C      paper, plastic
    
    (3 row(s) affected)
    

    编辑 将for xml path替换为for xml raw的查询,因此这应该适用于SQL Server 2000

    SELECT
        a.*,dt.CombinedValue
        FROM #TableA        a
            LEFT OUTER JOIN (SELECT
                                 c1.RowID
                                     ,STUFF(REPLACE(REPLACE(
                                              (SELECT 
                                                   ', ' + TypeOf as value
                                                   FROM (SELECT
                                                             a.RowID,a.Value1,a.Value2,b.TypeOf
                                                             FROM #TableA                 a
                                                                 LEFT OUTER JOIN #TableB  b ON a.RowID=b.RowID
                                                        ) c2
                                                   WHERE c2.rowid=c1.rowid
                                                   ORDER BY c1.RowID, TypeOf
                                                   FOR XML RAW
                                              )
                                             ,'<row value="',''),'"/>','')
                                       , 1, 2, '') AS CombinedValue
                                 FROM (SELECT
                                           a.RowID,a.Value1,a.Value2,b.TypeOf
                                           FROM #TableA                 a
                                               LEFT OUTER JOIN #TableB  b ON a.RowID=b.RowID
                                      ) c1
                                 GROUP BY RowID
                            ) dt ON a.RowID=dt.RowID
    

    输出,与原始查询相同