MSSQL Dynamic для цикла - PullRequest
       3

MSSQL Dynamic для цикла

0 голосов
/ 07 сентября 2018

Прежде всего, я ни в коем случае не гуру SQL, хотя я балуюсь ...

Моя проблема заключается в следующем; У меня есть большая база данных, содержащая таблицу для

  • iteminfo (описания основных предметов) (. [Itemno])

  • solditems (информация о продаже предмета) (. [Itemno]. [Ratecode]. [Кол-во]. [Цена])

  • цены (в основном цена продажи предметов) (. [Ratecode])

Мне нужно сгенерировать отчет (отчеты по кристаллам), который показывает для каждого предмета, сколько раз он был продан по каждой ставке, и общую стоимость за него. Проблема, с которой я сталкиваюсь, заключается в том, что таблица [тарифы] может быть разного размера для разных баз данных, и может быть добавлено больше тарифов или удалены неиспользуемые тарифы.

Я построил следующий SQL-запрос, который выдает эту информацию, но если в базе данных есть 10 разных скоростей, мне нужно 10 раз скопировать код ниже, что сделает запрос очень запутанным, и, конечно, это не так. t принимает во внимание 11-ую ставку и начинает возвращать нечетные значения, если убирается ставка (или хуже, если убирается номер 5, а 6 становится 5, 7 становится 6 и т. д.).

Примечание: s.itemno относится к основному запросу и является номером элемента для конкретного запрашиваемого элемента.

(select count(solditems.ratecode) 
 from solditems 
 where solditems.itemno = s.itemno 
   and solditems.ratecode = (
       select [ratecode] 
       from (
          select row_number() over (order by [ratecode]) as  rownumber, * 
          from dbo.rates
       ) as mytable 
       where rownumber = '**1**'
  )
) as ratqty**1**,


(select sum(solditems.qty * solditems.price) from solditems 
where solditems.itemno = s.itemno and solditems.ratecode = (select 
    [ratecode] from (select row_number() over (order by [ratecode]) as 
    rownumber, * from dbo.rates) as mytable where rownumber = '**1**')) 
as rateval**1**,
...

Если бы я использовал язык программирования, например C, я бы просто написал цикл for наподобие (псевдокод): int maxi = count (rate.ratecode) для i = 0 и i! = Maxi {делаю что-то для rownumber i} i ++ .

В1: Есть ли способ создать цикл for, подобный этому, в SQL (должен быть совместим с отчетами Crystal, поэтому чем проще, тем лучше).

Q2: Как мне избежать проблем, если ставка будет удалена, а последующие таблицы переместятся вверх (6 - 5, 7 - 6 и т. Д.).

Бонус Q: Выше код не очень элегантный, есть ли более простой способ?

Требуемый пример вывода:

itemno 1 был продан 5 раз по 1000 евро каждый с кодом 1, 1 раз за 100 евро под кодом 2 itemno 2 был продан 6 раз по 500 € каждый под кодом 1, и т.д.

sampleoutput

1 Ответ

0 голосов
/ 07 сентября 2018

Чтобы избежать циклов и вставки копий, вам понадобится dynamic pivot для двух агрегированных значений, подобных этому:

DECLARE
  @cmd NVARCHAR(MAX) = N'',
  @qty_cols NVARCHAR(MAX) = N'',
  @val_cols NVARCHAR(MAX) = N'',
  @n int = 0

    -- this can be done in any other manner like via spt_values or FOR XML and so on
    SELECT TOP 100 PERCENT
      @n += 1,
      @qty_cols += ', [rateqty-' + CAST(@n AS VARCHAR(10)) + ']',
      @val_cols += ', [rateval-' + CAST(@n AS VARCHAR(10)) + ']',
      @cmd += ', MAX([rateqty-' + CAST(@n AS VARCHAR(10)) + ']) as [rateqty-' + CAST(@n AS VARCHAR(10)) + ']'
       + ', MAX([rateval-' + CAST(@n AS VARCHAR(10)) + ']) as [rateval-' + CAST(@n AS VARCHAR(10)) + ']'
    FROM #rates r
    ORDER BY r.ratecode

set @cmd = 'SELECT ' + STUFF(@cmd, 1, 2, '') + '
  from 
  (
    select 
      ''rateqty-'' + CAST(rn AS VARCHAR(10)) ratecode_qty, 
      ''rateval-'' + CAST(rn AS VARCHAR(10)) ratecode_val, 
      rateqty, rateval
    from (
      select ratecode, rateqty, rateval, ROW_NUMBER() OVER(ORDER BY r.ratecode) rn
      from #rates r
    ) r
  ) r
  pivot (max(r.rateqty) for ratecode_qty in (' + STUFF(@qty_cols, 1, 2, '') + ' )) p1
  pivot (max(p1.rateval) for ratecode_val in (' + STUFF(@val_cols, 1, 2, '') + ' )) p2
  order by 1'

exec(@cmd)
;

Для этих образцов данных:

insert into rates(ratecode)
values (1), (2), (3)

insert into solditems(ratecode, qty, price)
values
 (1, 5, 22),
 (1, 2, 22),
 (3, 1, 33)

вывод будет:

| rateqty-1 | rateval-1 | rateqty-2 | rateval-2 | rateqty-3 | rateval-3 |
|-----------|-----------|-----------|-----------|-----------|-----------|
|         2 |       154 |         0 |         0 |         1 |        33 |

Полный источник здесь: http://sqlfiddle.com/#!18/6f390/42

Что касается вопроса об упрощении ваших подзапросов в отчете, где больше исходных таблиц, чем я предполагаю (так как вы показали только часть кода), то ratecodes должен быть CROSS JOIN ed для внешнего запроса (который звучит немного пугающе) или присоединяется к solditems напрямую и агрегируется (, а затем поворачивается) так:

SELECT 
  ...
FROM ... <outer query>
CROSS APPLY
(
  select
     r.ratecode,
     count(1) rateqty,  -- i don't quite understand what is this supposed to be; number of transactions?
     sum(si.qty * si.price) rateval
  from solditems si
  INNER JOIN dbo.rates r
   ON si.ratecode = r.ratecode
  where si.itemno = s.itemno
  group by r.ratecode
) s

upd: после публикации этого подзапроса я понял, что dbo.rates нигде не используется, кроме ROWNUMBER. Поэтому, если вы делаете поворот, здесь не требуется присоединение к rates:

SELECT 
  ...
FROM ... <outer query>
CROSS APPLY
(
  select
     si.ratecode,
     count(1) rateqty,
     sum(si.qty * si.price) rateval
  from solditems si
  where si.itemno = s.itemno
  group by si.ratecode
) s
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...