Изменить формат таблицы на сводную - PullRequest
0 голосов
/ 19 января 2020

В продолжение этого выпуска

Основной код:

CREATE TABLE params
(
    id_param smallint PRIMARY KEY,
    name varchar(50) NOT NULL
)

CREATE TABLE objects_params
(
    id_object int,
    id_param smallint NOT NULL,
    cdate smalldatetime,
    value int
)

INSERT INTO dbo.params (id_param, name)
VALUES (1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), 
       (5, 'e'), (6, 'f'), (7, 'g')

INSERT INTO dbo.objects_params (id_object, id_param, cdate, value)
VALUES (1, 1, '20191206',NULL), (1, 2,'20191212', 100), (1, 1, '20191201', 110),
       (2, 4, '20191211',120), (2, 1,'20190101', 130), (2, 3, '20191212', 140),
       (2, 4, '20191111',150), (2, 3,'20190201', 160), (2, 3, '20190312', 170),
       (2, 3, '20191201', 175),(2, 3, '20191202', 180), (2, 3, '20191203', 185),
       (2, 3, '20191204', 190)

Затем мне нужно получить список значений, ближайших к данной дате:

DECLARE @userdate DATETIME
SET @userdate='20191202'

DECLARE @names as VARCHAR(MAX)
SELECT @names =
COALESCE(@names + ', ','') + QUOTENAME(name)
FROM
   (SELECT distinct name
    FROM objects_params 
    JOIN params ON objects_params.id_param = params.id_param
   ) AS B;

With t_sql as ( 
    SELECT  id_object, objects_params.id_param, name, cdate, value  
    FROM objects_params 
    JOIN params ON objects_params.id_param = params.id_param)
    --Where value <> ''

SELECT id_object, id_param, name, cdate, value  
FROM 
    (
        SELECT RANK() OVER (PARTITION BY id_object, id_param ORDER BY abs(datediff(ss, @userdate,cdate)) ASC) AS DateRank, *
        FROM t_sql
        WHERE cdate < @userdate
    ) 
AS DetailsRanking
WHERE DetailsRanking.DateRank=1

и получите такую ​​таблицу:

id_object id_param   name      cdate      value
-----------------------------------------------
   1         1         a     2019-12-01    110
   2         1         a     2019-01-01    130
   2         3         c     2019-12-01    175
   2         4         d     2019-11-11    150

Но как мне получить результаты в следующем формате:

    id_object     a         b       c      d      e      f      g   
    ---------------------------------------------------------------
       1         110      null    null   null   null   null   null
       2         130      null    175    150    null   null   null

Все мои попытки использовать сводку в этот контекст оказался неудачным.


Обновление

По совету @Gordon Linoff и @xXx пытались переделать код для использования Dinami c SQL, поэтому здесь мы go:

USE [DConturDb]
GO

DECLARE @userdate VARCHAR(MAX)
SET @userdate='20191202';

DECLARE @names as VARCHAR(MAX)
SELECT @names =
COALESCE(@names + ', ','') + QUOTENAME(name)
FROM
   (SELECT name
    FROM params 
   ) AS B;

DECLARE @SQL as VARCHAR(MAX)
SET @SQL = 

'WITH op as (SELECT op.id_object, op.id_param, name, op.cdate, op.value, ROW_NUMBER () OVER (PARTITION BY op .id_object, op.id_param ORDER BY op.cdate DES C) как последовательность FROM objects_params op Параметры JOIN p ON op.id_param = p.id_param

  WHERE op.cdate <='''+ @userdate +'''
 )

SELECT id_object, '+ @names +' FROM (выберите id_object, value, name из op, где seqnum = 1) как tbl pivot (max (значение) для имени в ('+ @names +')) piv '

execute(@SQL)

Done.

Ответы [ 3 ]

2 голосов
/ 19 января 2020

Вы можете использовать условное агрегирование:

WITH op as ( 
      SELECT op.id_object, op.id_param, name, op.cdate, op.value,
             ROW_NUMBER() OVER (PARTITION BY op.id_object, op.id_param ORDER BY op.cdate DESC) as seqnum
      FROM objects_params op JOIN
           params p
           ON op.id_param = p.id_param
      WHERE op.cdate < @userdate
     )
SELECT id_object,
       MAX(CASE WHEN id_param = 1 THEN value END) as value_1,  
       MAX(CASE WHEN id_param = 2 THEN value END) as value_2, 
       . . .  
FROM op
WHERE seqnum = 1
GROUP BY id_object;
1 голос
/ 27 января 2020

Вы получите результат w sh.

DECLARE @userdate DATETIME
   SET @userdate='20191202'
   select * from (
   select b.id_object,value,a.name from params a
   INNER JOIN objects_params b on a.id_param = b.id_param
   where cdate < @userdate--here is your parameters
   ) as t 
   pivot (sum(value) for name IN ([a],[b],[c],[d],[e],[f],[g]) ) as Pivot_tbl --pivot based on names 
1 голос
/ 19 января 2020

SQL Fiddle - Просто замените getdate () на используемую вами переменную:

MS SQL Настройка схемы сервера 2017 * :

CREATE TABLE Result (id_object int,id_param int,name varchar(255)
                    ,cdate date,  val int);
INSERT INTO  Result(id_object,id_param,name,cdate,val)
            VALUES (1,1,'a','2019-12-01',110)
            ,(2,1,'a','2019-01-01',130)
            ,(2,3,'c','2019-12-01',175)
            ,(2,4,'d','2019-11-11',150)

Запрос 1 :

with CTE AS (select *,
(CASE WHEN name='a' THEN val  END) AS a,
(CASE WHEN name = 'b' THEN val END) AS b,
(CASE WHEN name='c' THEN val END) AS c,
(CASE WHEN name='d' THEN val END) AS d,
(CASE WHEN name='e' THEN val END) AS e,
(CASE WHEN name='f' THEN val END) AS f,
(CASE WHEN name='g' THEN val END) AS g,             
ROW_NUMBER() OVER (PARTITION BY id_param,id_object Order By cdate) as rn
from Result
             where cdate < getdate()
group by id_object,id_param,name,cdate,val              )

select c.id_object

,max(c.a) AS a
,max(c.b) AS b
,max(c.c) AS c
,max(c.d) AS d
,max(c.e) AS e
,max(c.f) AS f
,max(c.g) AS g

from cte c
where rn=1
group by c.id_object

Результаты :

| id_object |   a |      b |      c |      d |      e |      f |      g |
|-----------|-----|--------|--------|--------|--------|--------|--------|
|         1 | 110 | (null) | (null) | (null) | (null) | (null) | (null) |
|         2 | 130 | (null) |    175 |    150 | (null) | (null) | (null) |
...