【问题标题】:SQL Server query of dimensions containing history of changesSQL Server 查询包含更改历史的维度
【发布时间】:2020-07-08 20:39:06
【问题描述】:

我在 SQL Server 中有一个维度表,它以以下格式存储具有更改历史记录的值:

[Position_History]
Position          Person    Inserted     Deleted
Sales 1           Mark      2019-01-01   2019-02-01
Sales 2           Mark      2019-02-01   2019-05-01
Sales 3           Mark      2019-05-01   2020-07-01
Senior Developer  Peter     2019-01-01   2020-07-01
Junior Developer  John      2019-01-01   2019-04-01
Project Manager   John      2019-04-01   2020-07-01

插入 = 记录插入日期。

已删除 = 记录更新日期。

当查询特定日期的职位状态时,一个简单的 where 查询就足够了:

SELECT Position, Person FROM Position_History WHERE Inserted <= '2019-05-01' AND Deleted > '2019-05-01'

With result:

Position          Person
Sales 3           Mark 
Senior Developer  Peter
Project Manager   John

但是,我不知道使用哪种方法来创建一个可以返回日期列表结果的查询。例如,我想知道连续 3 个月的状态 - '2019-03-01'、'2019-04-01'、'2019-05-01'

我正在寻找的结果是:

Position          Person  Date
Sales 2           Mark    2019-03-01
Senior Developer  Peter   2019-03-01
Junior Developer  John    2019-03-01
Sales 2           Mark    2019-04-01
Senior Developer  Peter   2019-04-01
Project Manager   John    2019-04-01
Sales 3           Mark    2019-05-01
Senior Developer  Peter   2019-05-01
Project Manager   John    2019-05-01

我正在寻找能够动态接受参数作为参数列表或表的方法,避免对 x 查询进行硬编码联合。

感谢您的帮助!

【问题讨论】:

  • 您想要只在这三个日期的比赛状态,还是包括这两个日期之间每个日期的所有变化?

标签: sql sql-server


【解决方案1】:

如果我正确理解了您的请求,那么您的问题不在于查询的逻辑,而在于如何在多个日期运行该逻辑。既然如此,您可以将您想要的日期放入一个表格并加入它:

create table #dates(ReportingDate date);
insert #dates 
values ('2019-03-01'),
       ('2019-04-01'),
       ('2019-05-01');

select ph.Position, ph.Person, dt.ReportingDate
from   dbo.Position_History ph
join   #dates               dt on ph.Inserted <= dt.ReportingDate
                                  and ph.Deleted > dt.ReportingDate

如果您希望能够从应用程序中传递它,您可以使用table valued parameter,以及函数或存储过程。例如:

create type dbo.ReportingDates as table(ReportingDate date);
create function dbo.PositionsAtDates(@dates dbo.ReportingDates readonly) 
returns table as return
(
   select ph.Position, ph.Person, dt.ReportingDate
   from   dbo.Position_History ph
   join   @dates               dt on ph.Inserted <= dt.ReportingDate
                                     and ph.Deleted > dt.ReportingDate
);

-- usage within SSMS:
declare @d dbo.ReportingDates;
insert @d values ('2020-06-01'), ('2020-07-01');
select * from dbo.PositionsAtDates(@d);

如果您想在 C# 应用程序中使用它,您需要使用 SqlDbType = SqlDbType.Structured 创建一个参数,并且可以将其值设置为 DataTable。见this threadthis MS documentation

有关在 SQL 中传递“数组”的其他方法,请参阅Erland Sommarskog's guide

【讨论】:

  • 或者,比工会稍微整洁...INSERT INTO @d VALUES ('2020-06-01'),('2020-07-01'),('2020-08-01');
  • 啊,我有一个记事本++宏的坏习惯:)
  • 为什么我不记得使用内连接?
【解决方案2】:

我能想到的一种可能的方法是,

DB 小提琴 - https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=e4d980ca238769feef5bbb49c451a23d

出于演示目的,我已将日期值硬编码,但实际上您可以将这些值保存在单独的表中的某个位置。您还可以将 between 子句替换为 &gt;= AND &lt; 运算符以适当地包含/排除记录。

示例 SQL -

select mt.position, mt.person, dt.filter_date from (SELECT '2019-03-01'  as filter_date
UNION
SELECT '2019-04-01'
UNION 
Select '2019-05-01') dt
Join
(SELECT Position, Person, inserted, deleted FROM Position_History) mt
ON dt.filter_date between mt.inserted and mt.deleted;

如下图适当替换BETWEEN子句-

mt.inserted <= dt.filter_date and mt.deleted > dt.filter_date

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-18
    • 1970-01-01
    • 2019-01-27
    • 1970-01-01
    相关资源
    最近更新 更多