繁体   English   中英

SQL Server-来自存储过程的模拟记录

[英]SQL Server - Mock Records from Stored Procedure

抱歉,如果这是重复的内容-我已经在寻找答案,但是我发现的并不完全是我想要的。

我有一个非常简单的存储过程,可以从库存表中返回基于日期的库存。 它采用带有库存商ID的 开始日期结束日期参数。

查询如下所示...

select  
    S.Date, 
    S.Amount 
from Stock S
where 
    S.StockistID = @pi_stockistId and
    S.Date >= @pi_startDate and
    S.Date <= @pi_endDate

我需要确保的是,始终在请求的日期范围内返回一行-即使该日期和库存商的数据库中没有记录。 模拟记录的数量为零。

数据正被馈送到外部系统,因此,除非所述系统将值从零增加(我不需要在表中存储数千个空记录),否则我不需要插入记录。

如果随后来自外部系统的调用增加了金额,那么此时我将把记录插入到表中。

我知道我可以通过创建一个临时表变量并将不存在的记录插入结果集中来做到这一点-我只是想知道我是否可以在查询中做任何事情来防止不得不使用表变量。

再次抱歉,是否已经提出要求。

我正在使用SQL Server 2014。

谢谢

这是库存表中的数据示例

------------------------------------
| Date       | StockistId | Amount |
------------------------------------
| 12/04/2017 | 1          | 10     |
| 14/04/2017 | 1          | 20     |
------------------------------------

如果我查询库存商1的日期12/04/2017-14/04/2017,则会得到以下信息...

-----------------------
| Date       | Amount |
-----------------------
| 12/04/2017 | 10     |
| 14/04/2017 | 20     |
-----------------------

由于没有记录13/04/2017

我想返回的是...

-----------------------
| Date       | Amount |
-----------------------
| 12/04/2017 | 10     |
| 13/04/2017 | 0      |
| 14/04/2017 | 20     |
-----------------------

我的查询在此处“模拟”了13/04/2017的记录

如果您有日历表,这将成为一个非常简单的查询。

SELECT  c.Date,
        Amount = ISNULL(s.Amount, 0)
FROM    dbo.Calendar AS c
        LEFT JOIN Stock AS s
            ON s.Date = c.Date
            AND s.StockistID = @pi_stockistId
WHERE   c.Date >= @pi_startDate
AND     c.Date <= @pi_endDate;

如果您没有日历表,我建议您创建一个日历表,它们非常有用。 如果您无法创建一个,则可以很容易地即时生成一个:

首先只需使用交叉联接和ROW_NUMBER()生成一系列数字

WITH N1 AS (SELECT N FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n (N)),
N2 (N) AS (SELECT 1 FROM N1 AS N1 CROSS JOIN N1 AS N2),
N3 (N) AS (SELECT 1 FROM N2 AS N1 CROSS JOIN N2 AS N2)
SELECT  ROW_NUMBER() OVER(ORDER BY N) - 1
FROM    N3;

这将生成介于0-9999之间的数字,该数字应涵盖您需要的任何日期范围。

然后,您可以将这个数字与DATEADD一起使用以获取您所在范围内的日期:

DECLARE @pi_startDate DATE = '20170101',
        @pi_endDate DATE = '20170301';

WITH N1 AS (SELECT N FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n (N)),
N2 (N) AS (SELECT 1 FROM N1 AS N1 CROSS JOIN N1 AS N2),
N3 (N) AS (SELECT 1 FROM N2 AS N1 CROSS JOIN N2 AS N2),
Numbers (N) AS (SELECT ROW_NUMBER() OVER(ORDER BY N) - 1 FROM N3)

SELECT  TOP (DATEDIFF(DAY, @pi_startDate, @pi_endDate) + 1)
        Date = DATEADD(DAY, N, @pi_startDate)
FROM    Numbers;

这给你你的约会。

然后,对于最后一步,您只需要离开即可加入股票表

DECLARE @pi_startDate DATE = '20170101',
        @pi_endDate DATE = '20170301';

WITH N1 AS (SELECT N FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n (N)),
N2 (N) AS (SELECT 1 FROM N1 AS N1 CROSS JOIN N1 AS N2),
N3 (N) AS (SELECT 1 FROM N2 AS N1 CROSS JOIN N2 AS N2),
Calendar (Date) AS 
(   SELECT TOP (DATEDIFF(DAY, @pi_startDate, @pi_endDate) + 1)
            DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY N) - 1, @pi_startDate)
    FROM    N2
)
SELECT  c.Date,
        Amount = ISNULL(s.Amount, 0)
FROM    Calendar AS c
        LEFT JOIN Stock AS s
            ON s.Date = c.Date
            AND s.StockistID = @pi_stockistId;

由三部分组成的文章中提供了有关日历表以及生成无循环序列的更多信息:

尽管可以接受的答案非常好,但我想提供另一种动态创建日期的方法-使用递归CTE。 也许值得商,,但我会说它更简单。

DECLARE @startDate DATE = '20170401';
DECLARE @endDate DATE = '20170410';

WITH CTE_Dates AS 
(
    SELECT @StartDate AS Dt
    UNION ALL
    SELECT DATEADD(DAY,1,Dt)
    FROM CTE_Dates 
    WHERE Dt < @endDate
)
SELECT * 
FROM CTE_Dates
--LEFT JOIN to your data here
OPTION (MAXRECURSION 0); -- needed if range is more than 100 days

暂无
暂无

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

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