简体   繁体   English

存储过程要做两级枢纽

[英]Stored procedure to do a two-level pivot

I am trying to create a stored procedure in Sybase Adaptive Server Anywhere that will do a double pivot of table. 我正在尝试在Sybase Adaptive Server Anywhere中创建一个存储过程,该存储过程将完成表的双重操作。 I will first outline with some images what I am trying to accomplish. 我将首先用一些图像概述我要完成的工作。

The problem 问题

  1. Raw data 原始数据

Here is the raw data in the table; 这是表中的原始数据; in the sample code that I have posted lower down this is temporary table #t1 : 在我下面向下发布的示例代码中,这是临时表#t1:

在此处输入图片说明

  1. First level of pivoting 第一层次的透视

The first level of pivoting involves grouping on the column rownr and pivoting on the column col , putting the resulting table into temporary table #t2 : 数据rownr的第一级包括对rownr列进行分组和对列col rownr ,将结果表放入临时表#t2中:

在此处输入图片说明

I have the code up to this point which I have posted lower down. 到目前为止,我已经发布了下面的代码。

  1. Second level of pivoting 二级枢纽

This is the section that I am struggling with. 这是我正在努力的部分。 I am now needing to pivot table #t2 grouping on the column ObjectId and replicating the columns Operation and Code for the number of rows in the grouping to produce table #t3. 现在,我需要对ObjectId列上的表#t2分组进行透视,并为该分组中的行数复制列OperationCode ,以生成表#t3。 So the result for the example I have given would look like this: 因此,我给出的示例的结果将如下所示:

在此处输入图片说明

Because two columns are being replicated ( Operation and Code ) the number of columns in the resulting table should equal 2 multiplied by the number of rows in the grouping with the largest number of rows. 因为要复制两列(“ OperationCode ),所以结果表中的列数应等于2乘以分组中具有最大行数的行数。 Groupings that have less than the maximum number of grouped rows will be padded with null values, as seen in the example. 如示例所示, 小于分组行的最大数目的分组将被填充为空值。

The code 编码

Here is my code that creates the first two tables, #t1 and #t2 : 这是我的代码,用于创建前两个表#t1和#t2:

begin
    create table #t1(rownr int, col nvarchar(15), val nvarchar(300));
    insert into #t1 values(1, 'ObjectId', 'A');
    insert into #t1 values(1, 'Operation', 'Op1');
    insert into #t1 values(1, 'Code', '101');
    insert into #t1 values(2, 'ObjectId', 'A');
    insert into #t1 values(2, 'Operation', 'Op2');
    insert into #t1 values(2, 'Code', '102');
    insert into #t1 values(3, 'ObjectId', 'B');
    insert into #t1 values(3, 'Operation', 'Op3');
    insert into #t1 values(3, 'Code', '103');
    insert into #t1 values(4, 'ObjectId', 'B');
    insert into #t1 values(4, 'Operation', 'Op4');
    insert into #t1 values(4, 'Code', '104');
    insert into #t1 values(5, 'ObjectId', 'B');
    insert into #t1 values(5, 'Operation', 'Op5');
    insert into #t1 values(5, 'Code', '105');
    -- Create t2
    select 
        rownr,
        Max(case when col = 'ObjectId' then val end) as ObjectId,
        Max(case when col = 'Operation' then val end) as Operation,
        Max(case when col = 'Code' then val end) as Code
        into #t2
    from #t1
    group by rownr
    order by rownr, ObjectId;
    select * from #t2; 
    -- Create #t3    <--- This is where I need help
end

Take note 记笔记

Please note that I am trying to solve this for Sybase Adaptive Server Anywhere which does not have a pivot statement like Sql Server does, so a solution using a pivot statement will not help me. 请注意,我试图解决这一对Sybase Adaptive Server Anywhere 具有一个pivot声明像Sql Server呢,所以使用的解决方案pivot声明绝不会帮我。

You need each set of A, B, etc in a single temporary table with their ObjectIds, with an ordering integer 1,2,3,4 against the ObjectIDs, regardless of the string value of the op. 您需要在单个临时表中的每组A,B等及其ObjectId,对ObjectID的排序整数为1,2,3,4,而与op的字符串值无关。

Getting such an ordered integer is normally easy with an IDENTITY, but you don't want it for all, you want it per A,B,C etc subset. 有了IDENTITY,获得这样一个有序整数通常很容易,但是您并不想全部都想要它,而是希望每个A,B,C等子集都需要它。

Thus if you can run a Cursor on each ObjectId value (A,B,C,etc.,) and get the integer ordered operations for those into a temp table, then you can pivot easily with multiple outer joins. 因此,如果您可以对每个ObjectId值(A,B,C等)运行Cursor并将这些对象的整数排序操作放入临时表中,则可以轻松进行多个外部联接的数据透视。

So: create table #l(Id NUMERIC(8) IDENTITY, op VARCHAR(30), obj VARCHAR(300)) go 所以:创建表#l(Id NUMERIC(8)IDENTITY,op VARCHAR(30),obj VARCHAR(300))去

set identity_insert #l on

Get a cursor on the objectIds and loop like: 在objectIds上获取一个游标,然后像这样循环:

select  Id = IDENTITY(8)
    , t2.val op
    , t1.val obj
into 
existing table 
#l
from #t1 t1, #t1 t2
where t1.col = 'ObjectId'
and t1.val = 'A' -- this would be the cursors value
and t1.rownr = t2.rownr
and t2.col = 'Operation'

You will then find that #l can be pivotted nicely with multipler outers, because you'll have a table like: 然后,您会发现#l可以很好地与多个外部对象一起旋转,因为您将拥有一个像这样的表:

select * from #l order by 3,1
 Id          op     obj  
 ----------- ------ -----
           1 Op1    A    
           2 Op2    A    
           1 Op3    B    
           2 Op4    B    
           3 Op5    B    

Just in case this helps someone else, here is the code that I eventually came up with to accomplish the required double pivot: 以防万一这对其他人有帮助,下面是我最终想出的代码,以完成所需的双重工作:

begin
    DECLARE @nr_of_columns smallint;
    DECLARE @qry long varchar;
    DECLARE @i SMALLINT;
    DECLARE @createTable nvarchar(1000);
    create table #t1(rownr int, col nvarchar(15), val nvarchar(300));
    insert into #t1 values(1, 'ObjectId', 'A');
    insert into #t1 values(1, 'Operation', 'Op1');
    insert into #t1 values(1, 'Code', '101');
    insert into #t1 values(2, 'ObjectId', 'A');
    insert into #t1 values(2, 'Operation', 'Op2');
    insert into #t1 values(2, 'Code', '102');
    insert into #t1 values(3, 'ObjectId', 'B');
    insert into #t1 values(3, 'Operation', 'Op3');
    insert into #t1 values(3, 'Code', '103');
    insert into #t1 values(4, 'ObjectId', 'B');
    insert into #t1 values(4, 'Operation', 'Op4');
    insert into #t1 values(4, 'Code', '104');
    insert into #t1 values(5, 'ObjectId', 'B');
    insert into #t1 values(5, 'Operation', 'Op5');
    insert into #t1 values(5, 'Code', '105');
    -- create t2
    select 
        rownr,
        Max(case when col = 'ObjectId' then val end) as ObjectId,
        Max(case when col = 'Operation' then val end) as Operation,
        Max(case when col = 'Code' then val end) as Code
        into #t2
    from #t1
    group by rownr
    order by rownr, ObjectId;
    -- create #t3
  -- Maximum number of column groups in result table
  select max(cols) into @nr_of_columns from (SELECT count() over (partition by ObjectId) as cols from #t2) A;
  -- Create temporary table #t3 to hold results
  SET @i = 1;
  SET @createTable = 'create table #t3(ObjectId nvarchar(300)';
  while @i <= @nr_of_columns loop
    set @createTable = @createTable || ', Operation' || @i || ' nvarchar(300), Code' || @i || ' nvarchar(300)';
    set @i = @i + 1;
  end loop;
  set @createTable = @createTable || ')';
  execute immediate (@createTable);
  -- Pivot into #t3
  for whatever as cur cursor for 
    select 'insert into #t3 select ' || rw as qry from 
    (select '''' || A.ObjectId || ''' AS ObjectId, ' || LIST(attributes) || repeat(',null,null', @nr_of_columns-A.nr_in_group) AS rw from
     (SELECT ObjectId, count() over (partition by ObjectId) nr_in_group, row_number() over (partition by ObjectId order by Operation) nr, ''''||Operation||''' AS Operation' || nr || ',' || '''' || Code || ''' AS Code' || nr  as attributes FROM #t2 order by ObjectId,Operation) A
     group by ObjectId,@nr_of_columns, nr_in_group) B
   DO 
     execute IMMEDIATE (qry);
   end for;
  -- Output #t2
  select * from #t3;
end

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM