基于SQL Server中的唯一标识符合并多行(SQL Server的GROUP_CONCAT)?

时间:2015-09-17 18:51:04

标签: sql-server database sql-server-2008-r2

我最初是想通过PHP来解决这个问题,但是我运气不好......

How Can I Merge All Duplicates In Array Based On One Key's Value?

由于我没有找到解决方案,我决定尝试通过SQL查询来解决我的问题。我需要知道的是如何“合并”此查询中返回的行之间的差异?

SELECT
  Item.ID,
  Item.ItemLookupCode,
  nitroasl_pamtable.ManufacturerPartNumber,
  SupplierList.ReorderNumber,
  Item.Notes,
  Item.Description,
  Item.ExtendedDescription,
  Item.Quantity,
  nitroasl_pamtable.SpoofStock,
  Item.Price,
  nitroasl_pamtable.PAM_Keywords
FROM 
   Item
JOIN 
   nitroasl_pamtable ON Item.ID = nitroasl_pamtable.ItemID
JOIN 
   SupplierList ON Item.ID = SupplierList.ItemID
WHERE 
   (Item.ItemLookupCode LIKE '%tp-ac1750%' AND Price > 0.00 AND WebItem = 1)
   OR 
   (nitroasl_pamtable.ManufacturerPartNumber LIKE '%tp-ac1750%' 
    AND Price > 0.00 AND WebItem = 1)
   OR 
   (SupplierList.ReorderNumber LIKE '%tp-ac1750%' AND Price > 0.00 
    AND WebItem = 1)
   OR 
   (Item.Notes LIKE '%tp-ac1750%' AND Price > 0.00 AND WebItem = 1)
   OR 
   (Item.Description LIKE '%tp-ac1750%' AND Price > 0.00 AND WebItem = 1)
   OR 
   (Item.ExtendedDescription LIKE '%tp-ac1750%' AND Price > 0.00 
    AND WebItem = 1)
   OR 
   (nitroasl_pamtable.PAM_Keywords LIKE '%tp-ac1750%' AND Price > 0.00 
    AND WebItem = 1)
ORDER BY 
    Item.ItemLookupCode ASC;

我认为我需要(但尚未成功实施)

MySQL的GROUP_CONCAT等价物

我相信这个功能会做我需要的,但我使用的是SQL Server - 而不是MySQL。我似乎无法获得有关如何为我工作的发布解决方案......

我尝试了什么:

最近,我尝试了MAX()GROUP BY函数(一起),但它选择了重复行中返回的MAX值,因此返回一行每列中MAX个值。

SELECT
    MAX(Item.ID) AS Id,
    Item.ItemLookupCode,
    MAX(nitroasl_pamtable.ManufacturerPartNumber) AS ManufacturerPartNumber,
    MAX(SupplierList.ReorderNumber) AS ReorderNumber,
    MAX( CAST(Item.Notes AS varchar(max)) ) AS Notes,
    MAX(Item.Description) AS Description,
    MAX( CAST(Item.ExtendedDescription AS varchar(max)) ) AS ExtendedDescription,
    MAX(Item.Quantity) AS Quantity,
    MAX(nitroasl_pamtable.SpoofStock) AS SpoofStock,
    MAX(Item.Price) AS Price,
    MAX(nitroasl_pamtable.PAM_Keywords) AS PAM_Keywords,
    MAX(Item.PictureName) AS PictureName
FROM 
    Item
LEFT JOIN 
    nitroasl_pamtable ON Item.ID = nitroasl_pamtable.ItemID
LEFT JOIN 
    SupplierList ON Item.ID = SupplierList.ItemID
WHERE 
    (Item.ItemLookupCode LIKE '%tp-ac1750%' AND Price > 0.00 AND WebItem = 1)
    OR (nitroasl_pamtable.ManufacturerPartNumber LIKE '%tp-ac1750%' AND Price > 0.00 AND WebItem = 1)
    OR (SupplierList.ReorderNumber LIKE '%tp-ac1750%' AND Price > 0.00 AND WebItem = 1)
    OR (Item.Notes LIKE '%tp-ac1750%' AND Price > 0.00 AND WebItem = 1)
    OR (Item.Description LIKE '%tp-ac1750%' AND Price > 0.00 AND WebItem = 1)
    OR (Item.ExtendedDescription LIKE '%tp-ac1750%' AND Price > 0.00 AND WebItem = 1)
    OR (nitroasl_pamtable.PAM_Keywords LIKE '%tp-ac1750%' AND Price > 0.00 AND WebItem = 1)
GROUP BY 
    Item.ItemLookupCode
ORDER BY 
    Item.ItemLookupCode ASC

我希望将每列的所有返回值(用MAX丢弃)放入由逗号分隔的各自/原始列中,而不是丢弃每列的变体...

我需要什么:

Database Schema (Sample)

在上面的文件中,您将看到上述SQL查询返回的四行。我希望返回一行,如下所示:

ID

8265

ItemLookupCode:

TP-AC1750

ManufacturerPartNumber:

Archer C7

ReorderNumber:

7681617, ARCHERC7, N82E16833704177

备注:

TP-LINK Archer C7 AC1750 Routr

说明

TP-LINK Archer C7 AC1750 Routr

ExtendedDescription:

TP-Link Archer C7 Wireless-AC1750 Dual-Band Gigabit Router

数量:

0 (This should actually be a combined sum/total of the values in this column)

SpoofStock:

NULL (Same as Quantity - Should be sum / This value is different than Quantity)

价格:

129.95

PAM_Keywords:

NULL

我知道有更好的方法来编写此查询。我不是一个SQL人。此查询/脚本是一个关键字搜索,它返回Microsoft Dynamics RMS数据库中的项目,并输出我用来创建可以更改并重新提交到数据库的产品列表的JSON。我使用SQL Server 2008 R2(如果重要的话)。有关如何使用我的查询的某些变体来完成上述输出的任何建议将不胜感激!感谢

更新(SQLFiddle)

以下是一个链接到SQLFiddle:)

SQLFiddle with No MAX Function

SQLFiddle with MAX Function(由于丢失数据,这不是一个可行的解决方案)

2 个答案:

答案 0 :(得分:1)

这会让你开始,但nitroasl_pamtable表格仍然存在一些不确定性,所以我没有把它包括在内。

SELECT
  I.ID,
  I.ItemLookupCode,
  I.Notes,
  I.Description,
  I.ExtendedDescription,
  I.Quantity,
  I.Price,
  SL.ReorderNumbers,
  P.SpoofStock,
  P.ManufacturerPartNumber,
  P.PAM_Keywords
FROM
  Item I
  LEFT JOIN nitroasl_pamtable P
    ON I.ID = P.ItemID
  OUTER APPLY (
    SELECT
      ReorderNumbers = Substring((
        SELECT DISTINCT Convert(varchar(max), ', ' + SL.ReorderNumber)
        FROM SupplierList SL
        WHERE I.ID = SL.ItemID
        FOR XML PATH(''), TYPE
      ).value('.[1]', 'varchar(max)'), 3, 2147483647)
  ) SL
WHERE
  I.Price > 0.00
  AND I.WebItem = 1
  AND (
    I.ItemLookupCode LIKE '%tp-ac1750%'
    OR I.Notes LIKE '%tp-ac1750%'
    OR I.Description LIKE '%tp-ac1750%'
    OR I.ExtendedDescription LIKE '%tp-ac1750%'
    OR P.ManufacturerPartNumber LIKE '%tp-ac1750%'
    OR P.PAM_Keywords LIKE '%tp-ac1750%'
    OR EXISTS (
      SELECT *
      FROM dbo.SupplierList SL2
      WHERE
        I.ID = SL2.ItemID
        AND SL2.ReorderNumber LIKE '%tp-ac1750%'
    )
  )
ORDER BY
  I.ItemLookupCode ASC;

要正确引入nitroasl_pamtable,对于要连接的每个列,您可以执行新的OUTER APPLY。您可以执行单个OUTER APPLY以立即获取需要正常聚合的所有列(例如Sum())。

但是,我想提出这种串联会以一种可能导致错误评估或决策的方式模糊数据。从表中提取3个值并连接/求和它们将使它们看起来是一个单位,这可能不正确。

连接可能有害的另一种方式是重新排序数字。请注意,由于两个重复的重新订购号码,我在其中放置了DISTINCT - 但它们来自不同的供应商。那么除了可以从中获取供应商之外,重新订购号码还有什么用呢?如果两个不同的商品在不同的供应商处具有相同的再订货号怎么办? (例如,重新订购号BIGBOX是一家供应商的电视,但它是另一家供应商的巨型纸板箱。)

我不相信在查询中连接这些值是个好主意。相反,UI应该单独显示查询(项目作为一个行集,然后将每个其他表的支持数据作为单独的行集),然后以在UI中有意义的方式呈现数据。

答案 1 :(得分:0)

如果我理解正确,看起来你只需要连接ReorderNumber字段。您可以使用SQLCLR用户定义聚合(UDA)来完成此操作。

在SQLCLR函数,存储过程等SQL#库中有一个预先完成的UDA,称为 Agg_Join (我是作者,但是这个聚合函数)有免费版本)。使用它会使您的查询看起来如下:

SELECT
    MAX(Item.ID) AS Id,
    Item.ItemLookupCode,
    MAX(nitroasl_pamtable.ManufacturerPartNumber) AS ManufacturerPartNumber,
    SQL#.Agg_Join(SupplierList.ReorderNumber) AS ReorderNumber,
    MAX( CAST(Item.Notes AS varchar(max)) ) AS Notes,
    MAX(Item.Description) AS Description,
    MAX( CAST(Item.ExtendedDescription AS varchar(max)) ) AS ExtendedDescription,
    MAX(Item.Quantity) AS Quantity,
    MAX(nitroasl_pamtable.SpoofStock) AS SpoofStock,
    MAX(Item.Price) AS Price,
    MAX(nitroasl_pamtable.PAM_Keywords) AS PAM_Keywords,
    MAX(Item.PictureName) AS PictureName
FROM   Item
LEFT JOIN   nitroasl_pamtable
       ON Item.ID = nitroasl_pamtable.ItemID
LEFT JOIN  SupplierList
       ON Item.ID = SupplierList.ItemID
WHERE 
    (Item.ItemLookupCode LIKE '%tp-ac1750%' AND Price > 0.00 AND WebItem = 1)
    OR (nitroasl_pamtable.ManufacturerPartNumber LIKE '%tp-ac1750%'
         AND Price > 0.00
         AND WebItem = 1)
    OR (SupplierList.ReorderNumber LIKE '%tp-ac1750%'
          AND Price > 0.00
          AND WebItem = 1)
    OR (Item.Notes LIKE '%tp-ac1750%' AND Price > 0.00 AND WebItem = 1)
    OR (Item.Description LIKE '%tp-ac1750%' AND Price > 0.00 AND WebItem = 1)
    OR (Item.ExtendedDescription LIKE '%tp-ac1750%' AND Price > 0.00 AND WebItem = 1)
    OR (nitroasl_pamtable.PAM_Keywords LIKE '%tp-ac1750%' AND Price > 0.00 AND WebItem = 1)
GROUP BY      Item.ItemLookupCode
ORDER BY      Item.ItemLookupCode ASC;

简而言之,SQL#的完整版包含一个更强大的 Agg_Join 版本,名为 Agg_JoinPlus ,允许排序,过滤掉重复项,替换{{1} },更改分隔符等。

或者,如果你想自己创建这个,在这种情况下你可以自定义功能,我写了一篇文章,展示了创建一个用户定义的聚合的例子,只需要稍微修改一下就可以了连接:Getting The Most Out of SQL Server 2005 UDTs and UDAs(需要免费注册)。这是在SQL Server 2008发布之前编写的,它能够将NULL设置为MaxSize,以便它一次可以存储超过8000个字节(这对于这种类型的问题更为重要)操作比许多算术运算都要好。)

另一个不需要订阅且开箱即用的选项(我自己没有尝试过)是这个开源项目:

GROUP_CONCAT string aggregate for SQL Server

此项目自2013-05-09以来尚未更新,但我怀疑它会做你想要的并且适合你的查询,就像 SQL#.Agg_Join 和任何其他聚合一样。在安装脚本文件夹中有一个安装脚本 GroupConcatInstallation.sql ,其中包含Assembly和T-SQL包装器对象。

是的,所有这些聚合都应该与PHP或其他任何东西一起使用,因为它们是查询的一部分,因此它是执行处理的SQL Server,它与任何特定的客户端软件无关。