【问题标题】:i want a pivot query with no aggregates sql我想要一个没有聚合 sql 的数据透视查询
【发布时间】:2018-06-15 03:26:00
【问题描述】:

你好我有下表

ITEM            MarkRange
ENG-MA          20-39%
A1-014          40-59%
A2-10           1-9%
15-69           20-39%

但是我想要一个使我的结果看起来像这样的数据透视查询

 20-39%   40-59%  1-9%
 ENG-MA   A1-014  A2-10  
 15-69 

我已经使用 Item 列上的最大值编写了 PIVOT 查询,但这并没有奏效,因为它只返回了每个标记范围的一个项目。欢迎任何建议。谢谢

编辑:这是我的查询

select *
from 
(
  select ITEM, MarkRange
  from #unw
) src
pivot
(
  max(item )
  for MarkRange in ([1.9%], [20-39%], [10-19%])
) piv;

但是,当标记值包含大量项目时,每个标记只得到 1 行

【问题讨论】:

  • 显示您的查询,然后显示它是
  • 刚刚显示我的查询@PatrickArtner
  • 你想显示项目的连接值吗?

标签: sql sql-server tsql pivot aggregate


【解决方案1】:

一个已知标记范围的简单解决方案可能是使用条件聚合。

drop table t;
go
create table t(ITEM   varchar(20),         MarkRange varchar(20))
insert into t values 
('ENG-MA'  ,        '20-39%'),
('A1-014'  ,        '40-59%'),
('A2-10'   ,        '1-9%'),
('15-69'   ,        '20-39%')

select --rn,
    max(case when markrange = '1-9%' then item else '' end) as '1-9%',
    max(case when markrange = '20-39%' then item else '' end) as '20-39%',
    max(case when markrange = '40-59%' then item else '' end) as '40-59%'
from
(
select markrange, item , row_number() over (partition by markrange order by item) rn from t
) s
group  by rn

1-9%                 20-39%               40-59%
-------------------- -------------------- --------------------
A2-10                15-69                A1-014
                     ENG-MA               

(2 row(s) affected)

如果标记范围未知,则以编程方式创建 sql 语句并运行动态 sql。

【讨论】:

  • 简单,开门见山,恰到好处地回答了我的问题。谢谢
  • 不错的答案@PSalmon!我总是不愿意从聚合的角度来考虑这些操作,但这无疑是一种出色而简洁的方法。
【解决方案2】:

我认为您不能使用没有聚合的数据透视表。 另一种方法是使用 row_number 和完全连接。

类似这样的:

;WITH T AS (
    SELECT *
    FROM (VALUES
        ('ENG-MA','20-39%')
        ,('A1-014','40-59%')
        ,('A2-10','1-9%')
        ,('15-69','20-39%')
    ) AS V(ITEM, MarkRange)
)
, R1 AS (
SELECT
    ITEM
    , ROW_NUMBER() OVER (ORDER BY ITEM) AS ID
FROM T WHERE MarkRange = '20-39%'
)
, R2 AS (
SELECT
    ITEM
    , ROW_NUMBER() OVER (ORDER BY ITEM) AS ID
FROM T WHERE MarkRange = '40-59%'
)
, R3 AS (
SELECT
    ITEM
    , ROW_NUMBER() OVER (ORDER BY ITEM) AS ID
FROM T WHERE MarkRange = '1-9%'
)
SELECT R1.ITEM AS [20-39%], R2.ITEM AS [40-59%], R3.ITEM AS [1-9%]
FROM R1
FULL JOIN R2 ON R1.ID = R2.ID
FULL JOIN R3 ON R1.ID = R3.ID OR R2.ID = R3.ID
;

【讨论】:

  • 此解决方案将要求我指定项目和标记,这些值是动态填充的
  • 你可以使用动态SQL
【解决方案3】:

这是你想要的吗? Concatenating item values 低于标记范围?

我使用 STUFF 和 FOR XML PATH('') 方法来聚合以逗号分隔的范围内的项目。

--create table #unw( ITEM varchar(10), MarkRange varchar(10))
/*
insert into #unw values 
('ENG-MA','20-39%'),
('A1-014','40-59%'),
('A2-10','1-9%'),
('15-69','20-39%')
*/
;with cte as (
    select
        case when MarkRange = '1-9%' then item end as [1-9%],
        case when MarkRange = '20-39%' then item end as [20-39%],
        case when MarkRange = '40-59%' then item end as [40-59%]
    from #unw
)
select distinct
    stuff(
    (
        select isnull(',' + [1-9%],'')
        from cte
        for xml path('')
    ),1,1,'') as [1-9%],
    stuff(
    (
        select isnull(',' + [20-39%],'')
        from cte
        for xml path('')
    ),1,1,'') as [20-39%],
    stuff(
    (
        select isnull(',' + [40-59%],'')
        from cte
        for xml path('')
    ),1,1,'') as [40-59%]
from cte

查询的输出如下

我将逗号“,”替换为回车 + 换行符,如下所示

;with cte as (
    select
        case when MarkRange = '1-9%' then item end as [1-9%],
        case when MarkRange = '20-39%' then item end as [20-39%],
        case when MarkRange = '40-59%' then item end as [40-59%]
    from #unw
)
select
    replace(cast([1-9%] as varchar(30)), ',', CHAR(13)+CHAR(10))   [1-9%],
    replace(cast([20-39%] as varchar(30)), ',', CHAR(13)+CHAR(10)) [20-39%],
    replace(cast([40-59%] as varchar(30)), ',', CHAR(13)+CHAR(10)) [40-59%]
from
(
select distinct
    stuff(
    (
        select isnull(',' + [1-9%],'')
        from cte
        for xml path('')
    ),1,1,'') as [1-9%],
    stuff(
    (
        select isnull(',' + [20-39%],'')
        from cte
        for xml path('')
    ),1,1,'') as [20-39%],
    stuff(
    (
        select isnull(',' + [40-59%],'')
        from cte
        for xml path('')
    ),1,1,'') as [40-59%]
from cte
) t

【讨论】:

  • 接近完美的解决方案,但是所有项目都在一行中,有没有办法可以调整代码以使所有项目出现在不同的行中
  • 也许你可以用CHAR(13)+CHAR(10)替换','
【解决方案4】:

嗯,我今天看到的第二个问题需要类似的方法。

该方法的基本困难在于,每一列都被视为一个桶,其中项目应从顶部列出,并且每行的相邻值之间没有任何关系(除了它们的行号)。

请注意,此代码未经测试 - 我不确定是否可以在列标题中包含百分比符号。

此外,您需要参考一个数字表 - 如果您还没有数字表,这很容易实现,我将留给读者作为练习。

编辑:忘记在每个连接中包含 MarkRange 条件!

WITH base_data AS
(
    SELECT
        Item
        ,MarkRange
        ,ROW_NUMBER() OVER (PARTITION BY MarkRange ORDER BY Item) AS line_num

    FROM 
        #unw
)

,row_structure AS
(
    SELECT
        number AS line_num

    FROM
        numbers_table --you need to either reference a numbers table or a number sequence generator here

    WHERE
        number BETWEEN 1 AND (SELECT MAX(line_num) FROM base_data)
)

SELECT
    twenty_to_thirtynine.Item   AS [20-39%]
    ,forty_to_fiftynine.Item    AS [40-59%]
    ,one_to_nine.Item           AS [1-9%]

FROM 
    row_structure

LEFT JOIN
    base_data AS one_to_nine
    ON (one_to_nine.line_num = row_structure.line_num)
    AND (one_to_nine.MarkRange = '1-9%')

LEFT JOIN
    base_data AS twenty_to_thirtynine
    ON (twenty_to_thirtynine.line_num = row_structure.line_num)
    AND (twenty_to_thirtynine.MarkRange = '20-39%')

LEFT JOIN
    base_data AS forty_to_fiftynine
    ON (forty_to_fiftynine.line_num = row_structure.line_num)
    AND (forty_to_fiftynine.MarkRange = '40-59%')

ORDER BY
    row_structure.line_num

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-02
    • 2015-10-14
    • 1970-01-01
    • 2019-09-01
    • 1970-01-01
    相关资源
    最近更新 更多