【问题标题】:SQL: Convert a Narrow Table to a Wide TableSQL:将窄表转换为宽表
【发布时间】:2016-10-26 07:28:06
【问题描述】:

我有一个与已经问过的问题有点相似的问题:Mysql, reshape data from long / tall to wide

我的复杂性来自于我的数据结构可能是易变的,并且可以根据用户的判断进行更改。因此,我的列可以根据填充表的字段更改聚合(增长或缩小)。

对于我的表中可能包含的内容的示例,以下是一些记录:

输入:

LOCATION        User_Name        Title       Phone
Living Room     Joe Schmo        Worker      12-23
Baseball Park   Jane Doe         Worker      23-34
Backyard        Tiger Woods      Worker      34-45

但标题中存在相当多的可变性,甚至可能在位置中

输入:

LOCATION        User_Name        Title        Phone
Living Room     Batman           Manager      9112
Baseball Park   Batman           Manager      9112
Backyard        Batman           Manager      9112

所以我需要做的是,在匹配的位置上,我需要一个宽表,其中包含与位置关联的所有用户:

输出:

LOCATION        User_Name    Title   Phone   User_Name   Title     Phone
Living Room     Joe Schmo    Worker  12-23   Batman      Manager   9112
Baseball Park   Jane Doe     Worker  23-34   Batman      Manager   9112
Backyard        Tiger Woods  Worker  34-45   Batman      Manager   9112

任何建议提示将不胜感激。我试图复制这个例子,但显然我如何解释表格可以动态变宽或变窄的事实?我试图做枢轴,但我无法让它与多个字段一起工作。

想法?

【问题讨论】:

  • 请重新考虑您的想法 - 您的表目前正常化,您想要的是对表进行非规范化。想一想 - 如果你想得到一个简单的答案怎么办give me all users with a tile = 'Manager。目前非常简单,只需SELECT * FROM table WHERE title='Manager' - 如果Titlecolumn 上存在索引,则简单且非常有效。非规范化后它必须是:SELECT * FROM table WHERE title1='Manager' OR title2='Manager' OR ...... OR Ttile2876 = 'Manager'。查询很尴尬,而且速度很差。我强烈建议你放弃这个想法。
  • 哦,相信我,尝试这样做就像钉在黑板上一样。不幸的是,我要求将此表非规范化以进行演示。该演示文稿没有抽象层供我强迫他们为我对其进行非规范化。因此,需求被传递到数据库级别来执行。 TLDR;我想放弃这个,但我根本做不到。
  • 是否需要更改数据库中的表结构,或者只是生成看起来像您要求的输出? Normality 仅指数据库,而不是您创建的报告。对于您的问题:如果这是一项报告要求,通常最好通过报告应用程序而不是直接 SQL 来解决。
  • 我必须更改数据库中的结构。 COTS 产品是一种映射产品,需要严格的参数作为输入,我没有选择编写服务来为我完成此提升。它必须在数据库中。

标签: sql sql-server oracle


【解决方案1】:

您可以使用 PIVOT 查询来做到这一点。
您需要提前确定结果的宽度 - 我的意思是最终结果应该有多少列“组”(名称、标题、电话)。

下面的例子将整个表格分成了 3 组 (name+title+phone) 列:

WITH my_data AS (
    SELECT LOCATION, User_Name, Title, Phone,
           trunc( rn / 3 ) as group_number,
           rn - 3 * trunc( rn / 3 ) as rownum_within_group
    FROM (
        SELECT t.* ,
              row_number() over (partition by location order by user_name ) - 1 As rn
        FROM table1 t
    ) t
    -- ORDER BY Location, user_name
)
SELECT * 
FROM my_data
PIVOT (
   MAX( User_name) As user_name,
   MAX( Title ) as Title,
   MAX( Phone ) As Phone
   FOR rownum_within_group IN (0 as G0,1 As G1 ,2 As G2)
)
ORDER BY 1,2;

您的示例中稍作修改的数据的示例结果(我添加了额外的“Batman2”+“Batman3”+“Batman4”条目)是:

LOCATION       GROUP_NUMBER G0_USER_NAME    G0_TITLE    G0_PHONE    G1_USER_NAME    G1_TITLE    G1_PHONE    G2_USER_NAME    G2_TITLE    G2_PHONE
Backyard                 0  Batman          Manager     9112        Tiger Woods     Worker      34-45           
Baseball Park            0  Batman          Manager     9112        Jane Doe        Worker      23-34           
Living Room              0  Batman          Manager     9112        Batman2         Manager     9112        Batman3         Manager     9112
Living Room              1  Batman4         Manager     9112        Joe Schmo       Worker      12-23   

位置Living Room有5个用户,所以这个位置在结果集中分为两行,第一行有3个用户,第二行有2个用户(见上例最后两行) .

【讨论】:

  • 这绝对有效,我很惊讶你如何让它如此快速地工作。我想知道的一件事是如何没有有限数量的列或组。但我认为没有办法避免这种情况,因为数据透视表中需要必要的聚合函数?
猜你喜欢
  • 1970-01-01
  • 2020-12-27
  • 1970-01-01
  • 2011-10-05
  • 1970-01-01
  • 2021-09-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-19
相关资源
最近更新 更多