在查询中的行数中创建一行

时间:2017-06-05 06:41:53

标签: mysql sql-server

我有两张表,关系是一对多。 执行查询后,我得到了结果表:

+-------+------------------+-----------+-------+--------+
| index |       date       | sub_index | angle | length |
+-------+------------------+-----------+-------+--------+
|   681 | 19/10/2016 15:49 |         1 |    30 |    333 |
|   681 | 19/10/2016 15:49 |         1 |    60 |    666 |
|   682 | 19/10/2016 15:52 |         2 |     0 |     44 |
|   682 | 19/10/2016 15:52 |         2 |    45 |     77 |
|   682 | 19/10/2016 15:52 |         2 |    90 |     67 |
+-------+------------------+-----------+-------+--------+

但是用户要求信息是平的。如下:

+-------+------------------+-----------+--------+---------+--------+---------+--------+---------+
| index |       date       | sub_index | angle1 | length1 | angle2 | length2 | angle3 | length3 |
+-------+------------------+-----------+--------+---------+--------+---------+--------+---------+
|   681 | 19/10/2016 15:49 |         1 |     30 |     333 |     60 |     666 |        |         |
|   682 | 19/10/2016 15:52 |         2 |      0 |      44 |     45 |      77 |     90 |      67 |
+-------+------------------+-----------+--------+---------+--------+---------+--------+---------+

角度和长度的数量是未知的,可以是任意数量的项目。

我读到了关于PIVOT的内容,但我真的不知道如何在此示例中使用它,因为已更改的列(angle1angle2 ...)和因为我读到PIVOT需要一些函数(MAXCOUNT,...),我需要这个值......

2 个答案:

答案 0 :(得分:1)

要做到这一点,你在初始查询中缺少一个属性。让我们称之为类型。然后,您可以使用下面的查询来执行您想要的操作。

drop table if exists dbo.TableC;

create table dbo.TableC (
_Index int
, Date datetime
, Sub_index int
, Angle int
, Length int
, Type int
);


insert into dbo.TableC (_Index, Date, Sub_index, Angle, Length, Type)
values (681, CONVERT(datetime, '19/10/2016 15:49', 103), 1, 30, 333, 1)
, (681, CONVERT(datetime, '19/10/2016 15:49', 103), 1, 60, 666, 2)
, (682, CONVERT(datetime, '19/10/2016 15:52', 103), 2, 0, 44, 1)
, (682, CONVERT(datetime, '19/10/2016 15:52', 103), 2, 45, 77, 2)
, (682, CONVERT(datetime, '19/10/2016 15:52', 103), 2, 90, 67, 3)



select
    t._Index, t.Date, t.Sub_index
    , max(case when t.Type = 1 then t.Angle else null end) as Angle1
    , max(case when t.Type = 1 then t.Length else null end) as Length1
    , max(case when t.Type = 2 then t.Angle else null end) as Angle2
    , max(case when t.Type = 2 then t.Length else null end) as Length2
    , max(case when t.Type = 3 then t.Angle else null end) as Angle3
    , max(case when t.Type = 3 then t.Length else null end) as Length3
from dbo.TableC t
group by t._Index, t.Date, t.Sub_index

答案 1 :(得分:1)

这是一种可能的动态解决方案。请注意,结果表中的列数取决于输入数据,这通常是个坏主意。因此,将此代码放在存储过程中会很棘手。

尽管如此,这是一个TSQL片段,应该回答你的问题:

--create a custom type that will be used in the final dynamic call
if exists (select * from sys.types where name = 'TestTableType')
    drop type TestTableType
create type TestTableType as table([index] int,[sub_index] int, [data] nvarchar(max))

go

--this table contains your input data
declare @tem table([index] int, [date] datetime , [sub_index] int, [angle] decimal (19,6),[length] decimal (19,6))
--temp table used to calculate maximum number of columns to show
declare @counters table([index] int,[sub_index] int, [counter] int)
--temp table that will hold denormalized values (one row for each index/subindex couple)
declare @denormalized_data TestTableType
--this variables contains the maximum number of columns to show
declare @max_columns int
--this variable will contain the dunamically generated TSQL query that will give the final result
declare @dynamic_query nvarchar(max) 
--support variables used to generate dynamic query
declare @counter int 
declare @counter_str nvarchar(max) 

--1. populate input data
insert into @tem select  681 , '20161019 15:49',  1 ,   30,    333 
insert into @tem select  681 , '20161019 15:49',  1 ,   60,    666 
insert into @tem select  682 , '20161019 15:52',  2 ,    0,    44 
insert into @tem select  682 , '20161019 15:52',  2 ,    45,   77 
insert into @tem select  682 , '20161019 15:52',  2 ,    90,   67 
--insert into @tem select  682 , '20161019 15:52',  2 ,    8,    88 

--2. calculate the number of columns to show
insert into @counters
select [index],[sub_index], COUNT(*) from @tem group by [index],[sub_index]

select @max_columns =  max([counter]) from @counters

--3. denormalize data using an XML-based approach to obtain one row for each index/subindex couple
insert into @denormalized_data
SELECT 
  [index],[sub_index],
 '<MyData>'+ STUFF((
    SELECT '  <angle>' +  CAST([angle] AS VARCHAR(MAX))  + '</angle><length>' + CAST([length] AS VARCHAR(MAX)) +'</length>'
    FROM @tem 
    WHERE ([index] = Results.[index] and [sub_index] = Results.[sub_index]) 
    FOR XML PATH(''),TYPE).value('(./text())[1]','VARCHAR(MAX)')
  ,1,2,'') +  '</MyData>' AS NameValues
FROM @tem Results
GROUP BY [index],[sub_index]

--4. generate a dynamic TSQL query with the correct number of columns
set @counter = 0
set @dynamic_query=' SELECT [index],[sub_index],   '
while       @counter < @max_columns
  begin
     set @counter = @counter +1
     set @counter_str =  CAST(@counter  as nvarchar(max))
     set @dynamic_query = @dynamic_query + ' CONVERT(XML,[data]).value(''/MyData[1]/angle['+  @counter_str + ']'',''varchar(100)'') AS angle'+  @counter_str + ',  '
     set @dynamic_query = @dynamic_query + ' CONVERT(XML,[data]).value(''/MyData[1]/length['+  @counter_str + ']'',''varchar(100)'') AS length'+  @counter_str + ',  '
  end

set @dynamic_query = substring(@dynamic_query,1,LEN(@dynamic_query) - 1) + ' FROM @denormalized_d ' 

exec sp_executesql @dynamic_query, N'@denormalized_d TestTableType readonly', @denormalized_data

以下是具有3个不同值的输出:

enter image description here

以下是具有4个不同值的输出(只需取消注释行--insert into @tem select 682 , '20161019 15:52', 2 , 8, 88):

enter image description here