将一列变成几列需要什么SQL魔法?

时间:2010-12-19 14:05:57

标签: sql relational-database

我需要打印一些门票,每张门票都有足够的空间容纳一组客户详细信息以及该客户订购的最多五件商品的代码。订购超过五件商品的客户可获得多张票。所以从这样的订单表中,

Customer | Item
---------|------
Bob      | FTMCH
Bob      | ZORP
Bob      | KLUGE
Carol    | FTMCH
Carol    | MEEP
Carol    | ZORP
Ted      | FOON
Ted      | SMOCK
Alice    | ORGO
Carol    | SQICK
Carol    | BLECH
Carol    | KLUGE
Carol    | GLURP

我需要一个返回此内容的查询:

Customer | Item1 | Item2 | Item3 | Item4 | Item5
---------|-------|-------|-------|-------|------
Alice    | ORGO  | null  | null  | null  | null
Bob      | FTMCH | ZORP  | KLUGE | null  | null
Carol    | FTMCH | MEEP  | ZORP  | SQICK | BLECH
Carol    | KLUGE | GLURP | null  | null  | null
Ted      | FOON  | SMOCK | null  | null  | null

有些灵魂可以帮助我使用SQL吗? OpenOffice.org Base中的HSQL嵌入式数据库,如果它有所作为。

5 个答案:

答案 0 :(得分:2)

好的,这很好用:

SELECT
    "Customer",
    MAX(CASE WHEN "Slot" = 0 THEN "Item" END) AS "Item1",
    MAX(CASE WHEN "Slot" = 1 THEN "Item" END) AS "Item2",
    MAX(CASE WHEN "Slot" = 2 THEN "Item" END) AS "Item3",
    MAX(CASE WHEN "Slot" = 3 THEN "Item" END) AS "Item4",
    MAX(CASE WHEN "Slot" = 4 THEN "Item" END) AS "Item5"
FROM (
    SELECT
        l."Customer" AS "Customer",
        l."Item" AS "Item",
        COUNT(r."Item") / 5 AS "Ticket",
        MOD(COUNT(r."Item"), 5) AS "Slot"
    FROM "Orders" AS l
    LEFT JOIN "Orders" AS r
    ON r."Customer" = l."Customer" AND r."Item" < l."Item"
    GROUP BY "Customer", "Item"
)
GROUP BY "Customer", "Ticket"
ORDER BY "Customer", "Ticket"

这样做:

Customer | Item1 | Item2 | Item3 | Item4 | Item5 
---------|-------|-------|-------|-------|-------
Alice    | ORGO  |       |       |       |       
Bob      | FTMCH | KLUGE | ZORP  |       |       
Carol    | BLECH | FTMCH | GLURP | KLUGE | MEEP  
Carol    | SQICK | ZORP  |       |       |       
Ted      | FOON  | SMOCK |       |       |       

感谢所有帮助过的人,无论是在这里还是在Ask Metafilter.

(后续编辑:)

耶稣,这变得更糟: - (

原则上,业务规则允许同一客户多次订购相同的商品,并且所有未完成的订单都将包含在一组商店中。所以我的玩具桌应该看起来更像这样:

ID  | Customer | Item 
159 | Bob      | FTMCH
264 | Bob      | ZORP 
265 | Bob      | KLUGE
288 | Carol    | FTMCH
314 | Carol    | MEEP 
323 | Carol    | ZORP 
327 | Ted      | FOON 
338 | Ted      | SMOCK
358 | Alice    | ORGO 
419 | Carol    | SQICK
716 | Carol    | MEEP 
846 | Carol    | BLECH
939 | Carol    | MEEP 
950 | Carol    | GLURP
979 | Carol    | KLUGE

Carol的多个MEEP在原始解决方案中排名逻辑,我最终得到了以下可怕的怪物:

SELECT
    "Customer",
    MAX(CASE WHEN "Slot" = 0 THEN "Item" END) AS "Item0",
    MAX(CASE WHEN "Slot" = 1 THEN "Item" END) AS "Item1",
    MAX(CASE WHEN "Slot" = 2 THEN "Item" END) AS "Item2",
    MAX(CASE WHEN "Slot" = 3 THEN "Item" END) AS "Item3",
    MAX(CASE WHEN "Slot" = 4 THEN "Item" END) AS "Item4",
    MAX(CASE WHEN "Slot" = 0 THEN "Quantity" END) AS "Qty0",
    MAX(CASE WHEN "Slot" = 1 THEN "Quantity" END) AS "Qty1",
    MAX(CASE WHEN "Slot" = 2 THEN "Quantity" END) AS "Qty2",
    MAX(CASE WHEN "Slot" = 3 THEN "Quantity" END) AS "Qty3",
    MAX(CASE WHEN "Slot" = 4 THEN "Quantity" END) AS "Qty4"
FROM (
    SELECT
        "Customer",
        "Item",
        COUNT("ID") AS "Quantity",
        "Rank" / 5 AS "Ticket",
        MOD("Rank", 5) AS "Slot"
    FROM (
        SELECT
            main."ID" AS "ID",
            main."Customer" AS "Customer",
            main."Item" AS "Item",
            COUNT(less."Item") AS "Rank"
        FROM "Orders" AS main
        LEFT JOIN (
            SELECT DISTINCT
                "Customer",
                "Item"
            FROM "Orders") AS less
        ON less."Customer" = main."Customer" AND less."Item" < main."Item"
        GROUP BY "ID", "Customer", "Item"
    )
    GROUP BY "Customer", "Item", "Rank"
)
GROUP BY "Customer", "Ticket"

这使得:

Customer | Item0 | Item1 | Item2 | Item3 | Item4 | Qty0 | Qty1 | Qty2 | Qty3 | Qty3 | Qty4
Bob      | FTMCH | KLUGE | ZORP  |       |       | 1    | 1    | 1    |      |      |     
Carol    | BLECH | FTMCH | GLURP | KLUGE | MEEP  | 1    | 1    | 1    | 1    | 1    | 3   
Carol    | SQICK | ZORP  |       |       |       | 1    | 1    |      |      |      |     
Ted      | FOON  | SMOCK |       |       |       | 1    | 1    |      |      |      |     
Alice    | ORGO  |       |       |       |       | 1    |      |      |      |      |     

我认为它完成了这项工作,但我感觉非常幸运,所涉及的数据库总是很小(几千行)。

精神上我是一个嵌入式系统的人,而不是数据库人。任何以此为生的人能否告诉我这种废话是否常见?具有四个嵌套SELECT和LEFT JOIN的查询是否值得在Daily WTF中提及?

答案 1 :(得分:1)

我相信这只适用于T-SQL,但您可以使用PIVOT:http://msdn.microsoft.com/en-us/library/ms177410.aspx

我做了类似的事情,日期列表成为计算的列。

答案 2 :(得分:0)

不完全是你提出的问题,而不是MySQL而不是OpenOffice,但可能会给你一个想法或其他人可以使用它:

select
    u.Customer,
    group_concat(u.Item) items
from
    (select
        t.Item,
        @n:=if(@c=t.Customer and @n<4,@n+1,0) c1,
        @m:=if(@n,@m,@m+1) g,
        @c:=t.Customer as Customer
    from
        t1 t, (select @m:=0) init
    order
        by t.Customer
    ) u
group by
    u.g

输出:

+----------+------------------------------+
| Customer | items                        |
+----------+------------------------------+
| Alice    | ORGO                         | 
| Bob      | FTMCH,ZORP,KLUGE             | 
| Carol    | KLUGE,ZORP,BLECH,SQICK,GLURP | 
| Carol    | MEEP,FTMCH                   | 
| Ted      | FOON,SMOCK                   | 
+----------+------------------------------+

答案 3 :(得分:0)

这可以帮助您完成大部分工作,但不会处理Carol的重复订单。如果还有其他内容需要分组,例如OrderIDOrderDate,则很容易做到这一点。你能发布完整的架构吗?

select m1.Customer, 
    min(m1.Item) as Item1, 
    min(m2.item) as Item2, 
    min(m3.item) as Item3, 
    min(m4.item) as Item4, 
    min(m5.item) as Item5
from CustomerOrder m1
left outer join CustomerOrder m2 on m1.Customer = m2.Customer 
    and m2.item > m1.item
left outer join CustomerOrder m3 on m1.Customer = m3.Customer 
    and m3.item > m2.item
left outer join CustomerOrder m4 on m1.Customer = m4.Customer 
    and m4.item > m3.item
left outer join CustomerOrder m5 on m1.Customer = m5.Customer 
    and m5.item > m4.item
group by m1.Customer

<强>输出:

Customer       Item1      Item2      Item3      Item4      Item5
-------------- ---------- ---------- ---------- ---------- ----------
Alice          ORGO       NULL       NULL       NULL       NULL
Bob            FTMCH      KLUGE      ZORP       NULL       NULL
Carol          BLECH      FTMCH      GLURP      KLUGE      MEEP
Ted            FOON       SMOCK      NULL       NULL       NULL

答案 4 :(得分:0)

该要求并不罕见,可以在SQL中合理地提供。但是你有两个阻止你的问题。

1)您已输入SQL标记,即ISO / IEC / ANSI标准SQL。使用的正确方法是游标或光标替代(while循环,它做同样的事情,但更快)。这避免了所有这些外部连接和处理大量结果集;然后用GROUP BYs等打败它。它还处理重复,主要是因为它首先创建它们(通过别名表的五个版本)。是的,它会继续变得更糟,当数据库合理填充时,它将是一个性能损失。

2)关系数据库中不允许重复,即。在源表中;您需要使行唯一(并且不显示那些键/列)。尝试通过代码消除重复项是没有用的。如果更正了,则可以消除所有重复项(真实的和由不良代码创建的)。

使用子查询也可以更优雅地提供此要求;除了在这里你需要两个级别的嵌套,一个用于构建教学项目列,两个用于获得等级或位置。而且(标准SQL构造)预先假定您有一个Relational数据库(没有重复的行)。如果您不熟悉SQL,请选择High Eek因子。这就是大多数编码人员使用游标或光标替代的原因。

但是如果你没有SQL,它的基本功能(HSQL是一些低于标准的实现),那么我们就不会使用相同的工具包。我可以提供的SQL代码不会为您运行,我们会不停地来回运行。

(也许我们应该有一个“psuedo-SQL”标签。)

ID列防止重复???

由于数据库初学者编写的书籍,有一个神话在行业的某些部分普遍存在。像往常一样,神话没有科学依据。我们来试试吧。

    CREATE TABLE Person (
    PersonId  IDENTITY NOT NULL
        PRIMARY KEY,
    FirstName CHAR(30) NOT NULL,
    LastName  CHAR(30) NOT NULL
    )
INSERT Person VALUES ("Fred", "Astaire") 1 row(s) affected
INSERT Person VALUES ("Ginger", "Rogers") 1 row(s) affected
INSERT Person VALUES ("Fred", "Astaire") 1 row(s) affected
SELECT * FROM Person
PersonId FirstName LastName ======== ============================== ============================== 1 Fred Astaire 2 Ginger Rogers 3 Fred Astaire
3 row(s) affected
这是一个纯粹的,无法控制的重复行。简单的事实是。 Id列提供行号,但不会阻止重复行。为此,您需要在列上确定唯一性的唯一索引,如数据模型中所标识的,对于数据库中的每个关系表(根据定义,如果行不是唯一的,则不是关系表)。否则它只是一个文件存储系统。
    CREATE UNIQUE NONCLUSTERED INDEX U_Name
       ON Person (LastName, FirstName)

还有另一种形式的数据完整性(复制),我可能会在其中找到它。

    INSERT Person VALUES ("Fred", "Astair")
1 row(s) affected
INSERT Person VALUES ("Astaire", "Fred") 1 row(s) affected
所有这些都可以在SQL中预防。