MSSQL Dynamic for循环

时间:2018-09-07 07:02:44

标签: sql-server crystal-reports

首先,尽管我还涉猎……但我绝不是SQL专家。

我的问题如下:我有一个大型数据库,其中包含用于

的表
  • iteminfo(基本商品说明)(。[itemno])

  • solditems(关于商品销售的信息)(。[itemno]。[ratecode]。[qty]。[price])

  • 费率(基本上是商品的销售价格)(。[ratecode])

我需要生成一个报告(水晶报告),以显示每个商品在每种价格下已售出多少次以及该商品的总价值。我遇到的问题是[rates]表对于不同的数据库可能大小不同,并且可以添加更多的速率,或者可以删除未使用的速率。

我建立了以下SQL查询来获取此信息,但是如果数据库中有10种不同的比率,我需要将代码复制粘贴到10次以下,从而使查询非常混乱,当然,它不会t会考虑第11个比率,并且如果除去比率(或更糟的是,如果比率nr 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(rates.ratecode)for i = 0和i!= maxi {为rownumber i做一些事情} i ++

Q1:有没有一种方法可以像SQL中那样构建for循环(必须与Crystal报表兼容,因此越简单越好)。

第2季度:如果取消汇率,随后的表格上升到某个位置(6变为5,7变为6,依此类推),我将如何避免出现问题。

奖金问:上面的代码不是很优雅,有没有更简单的方法?

请求的示例输出:

itemno 1在价码1下已售出5次,每次售价€1000 itemno 2在价码1下已售出6次,每次售价€500。 等

sampleoutput

1 个答案:

答案 0 :(得分:0)

为避免循环和粘贴粘贴,您需要一个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并进行汇总(先是然后是),如下所示:

solditems

upd:发布此子查询后,我意识到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 dbo.rates之外均未使用。因此,如果您要进行数据透视,则无需在此处加入ROWNUMBER的链接:

rates