【问题标题】:Select multiple rows from SQL into one row in subquery从 SQL 中选择多行到子查询中的一行
【发布时间】:2013-02-26 13:42:57
【问题描述】:

我有这样的查询:

DECLARE @razem VARCHAR(MAX);

SELECT  Ordering.orderID , 
        Document.number, 
        (User_info.name  +' '+ User_info.surname),   
        Ordering.dateStart, 
        Ordering.dateEnd ,   
        (
            select   COALESCE(' ',@razem)+sell_type.name as r  
            from    Ordering_sell_type, Sell_type 
            where   orderID = Ordering.orderID and 
                    Ordering_sell_type.sell_typeID = sell_type.sell_typeID
        ) podz
FROM    Ordering, User_info, Product_Document, Document, Document_type   
WHERE   Ordering.orderID = Product_document.orderID  
        AND Document.documentID = Document_type.documentID  
        AND Document.documentID = Product_document.documentID  
        AND  Ordering.userID = User_info.userID   
        AND Ordering.isClosed = 1 AND Document_type.typeID = 1   
GROUP   BY  Document.isitfiscal, Document.refDocID, 
            Document.number, Ordering.orderID, User_info.name, 
            User_info.surname, Ordering.dateStart, 
            Ordering.dateEnd , Ordering.isCLosed  
ORDER   BY Ordering.dateEnd

在那个 COALESCE 函数中,我想获取所选订单的所有支付类型 - 例如,orderID 123 有 payTypes = Card、Cash,orderID 有 payTypes = Cash。

问题是我想将它放在一个简单的行中作为主查询的最后一行,例如: orderID, Document.number, UserInfo.name+surname, dateStart, dateEnd, ->card, cash

Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.

因为它返回不止一行。是否有可能在子查询中获取支付类型并作为一个字符串返回?

【问题讨论】:

  • 您使用的是哪个 RDBMS?
  • 在 oracle 中:WM_CONCAT、LISTAGG 或 XMLAGG 函数会将共享相似数据的多行转换为一个列表。 Coalese 一次只工作一排,不合并行。我相信 SQL 服务器中的XMLpath 有类似的组合;我认为 mySQL 使用Group_Concat

标签: sql sql-server coalesce


【解决方案1】:

根据您使用的语法,我假设您使用的是 SQL-Server,因此您可以使用 SQL-Servers XML 扩展来连接字符串。

SELECT  Ordering.orderID, 
        Document.number, 
        [UserName] = User_info.name  +' '+ User_info.surname,   
        Ordering.dateStart, 
        Ordering.dateEnd,
        [podz] = STUFF((    SELECT  DISTINCT ' ' + SellType.Name
                            FROM    Ordering_Sell_Type
                                    INNER JOIN Sell_Type
                                        ON Sell_Type.sell_typeID = Ordering_Sell_Type.sell_typeID
                            WHERE   Ordering.OrderID = Ordering_SellType.OrderId
                            FOR XML PATH(''), TYPE
                        ).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
FROM    Ordering
        INNER JOIN User_Info
            ON Ordering.UserID = User_Info.UserID
        INNER JOIN ProductDocument
            ON Ordering.OrderID = Product_Document.OrderID
        INNER JOIN Document
            ON Document.DocumentID = Product_Document.DocumentID
        INNER JOIN Document_Type
            ON Document_Type.DocumentID = Document.DocumentID
WHERE   Ordering.IsClosed = 1
AND     Document_Type.TypeID = 1
ORDER BY Ordering.dateEnd;

注意我已经用 ANSI 92 替换了所有的 ANSI 89 连接,因为这是更现代的语法,并且 generally accepted 是更易读的选项(我说普遍接受,因为它当然是个人喜好,还有在某些情况下,Oracle 更好地优化了 ANSI89 连接)。

编辑

看到您的数据重复来自 Product_Document 表,您可以使用以下方法删除这些数据:

SELECT  Ordering.orderID, 
        Document.number, 
        [UserName] = User_info.name  +' '+ User_info.surname,   
        Ordering.dateStart, 
        Ordering.dateEnd,
        [podz] = STUFF((    SELECT  DISTINCT ' ' + SellType.Name
                            FROM    Ordering_Sell_Type
                                    INNER JOIN Sell_Type
                                        ON Sell_Type.sell_typeID = Ordering_Sell_Type.sell_typeID
                            WHERE   Ordering.OrderID = Ordering_SellType.OrderId
                            FOR XML PATH(''), TYPE
                        ).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
FROM    Ordering
        INNER JOIN User_Info
            ON Ordering.UserID = User_Info.UserID
        INNER JOIN 
        (   SELECT  DISTINCT OrderID, DocumentID
            FROM    Product_Document
        ) Product_Document
            ON Ordering.OrderID = Product_Document.OrderID
        INNER JOIN Document
            ON Document.DocumentID = Product_Document.DocumentID
        INNER JOIN Document_Type
            ON Document_Type.DocumentID = Document.DocumentID
WHERE   Ordering.IsClosed = 1
AND     Document_Type.TypeID = 1
ORDER BY Ordering.dateEnd;

【讨论】:

  • 如果我没有猜对 DBMS,还有很多其他方法回答了 here
  • 重复来自哪里,每个订单是否有多个文档类型为 1 的文档?
  • 不,每个订单只有一个文件。就是这样,订单只有在没有关闭的时候才是订单。在我们关闭它( - 顺序)后,它变成了文档(并出现在文档表中)
  • 在查询中使用 DISTINCT 并没有什么问题,但通常最好隔离重复的来源并在那里处理它们。找到它们的一个好方法是使用 SELECT * 运行相同的查询,并查看在上面列出的列相同的行中哪里有不同的值。
【解决方案2】:

这篇 SO 文章/答案很好地讨论了将一系列 varchar 行汇总/聚合到单个列中的不同方法,就像您在谈论 https://stackoverflow.com/a/2410524/283895

【讨论】:

    【解决方案3】:

    是的。但这取决于您使用的 RDBMS。

    为了在一行中生成数据列表,您必须使用特定于您使用的 RDBMS 的分析函数,或者编写您自己的特定函数。

    在 oracle 中:WM_CONCAT、LISTAGG 或 XMLAGG 函数会将共享相似数据的多行转换为一个列表。

    Coalese 一次只能使用一行,并且不会合并行。

    • ORACLE:WM_CONCAT、LISTAGG、XMLAGG
    • SQL 服务器:XML 路径
    • My SQL:Group_Concat

    用途/语法各不相同。所以我建议根据您的 RDBMS 搜索这些术语。

    【讨论】:

      猜你喜欢
      • 2023-03-28
      • 1970-01-01
      • 1970-01-01
      • 2019-04-04
      • 1970-01-01
      • 2020-06-24
      • 1970-01-01
      • 2021-07-23
      • 1970-01-01
      相关资源
      最近更新 更多