需要一些棘手的SQL查询提示

时间:2018-01-15 10:09:45

标签: sql-server tsql

我遇到了一个SQL挑战。我的情况是这样的。

我有一个名为#Balance的表,其中我有历史数据(总是versionid = 0)和预测数据(增加的版本ID> 0)。

每个月都会产生新的预后。

因此,在2017年12月,我们有截至2017年11月的历史数据,但预计将于2017年12月和未来几个月创建预测153。

在2018年1月,我们有截至2017年12月的历史数据,并且从2018年1月开始新的预后,所以它会继续。

但麻烦来自报道。因为每个预后都必须包括所有历史月份,直至预测开始月份。

下面你看一个例子。

版本表已简化,它有更多行,因此是平衡。我想要的是最终结果,其中包含历史数据,但也会在预测开始月份之前为所有预测增加历史记录。

SQL代码应该是通用的,因此它可以为更多版本ID生成结果表。我需要使用来自版本的historyTo和来自Balance的(AYear * 100 + APer)来查找结果。

有人可以帮助我吗?

谢谢

关心Geir

Version
x------------x------------------------x
|     Id     |   Name    | HistoryTo  |
x------------x------------------------x
|    0       |  History  |   200701   |
|    153     |  Nov 2017 |   201711   |
|    154     |  Des 2017 |   201712   |
x------------x-----------x------------x

Balance
x------------x--------------------x---------x
| VersionId  |   AYear |   APer   | Amount  |
x------------x--------------------x---------x
|    0       |  2017   |   10     |  29327  |
|    0       |  2017   |   11     |    351  |
|    0       |  2017   |   12     |   6530  |
|  154       |  2018   |    1     |  25000  |
|  154       |  2018   |    2     |  39136  |
|  154       |  2018   |    3     |  20000  |
|  153       |  2017   |   12     |   3000  |
|  153       |  2017   |    1     |  47000  |
|  153       |  2017   |    2     |  35000  |
x------------x---------x----------x---------|

Result
x------------x--------------------x---------x
| VersionId  |   AYear |   APer   | Amount  |
x------------x--------------------x---------x
|    0       |  2017   |   10     |  29327  |
|    0       |  2017   |   11     |    351  |
|    0       |  2017   |   12     |   6530  |
|  154       |  2017   |   10     |  29327  |
|  154       |  2017   |   11     |    351  |
|  154       |  2017   |   12     |   6530  |
|  154       |  2018   |    1     |  25000  |
|  154       |  2018   |    2     |  39136  |
|  154       |  2018   |    3     |  20000  |
|  153       |  2017   |   10     |  29327  |
|  153       |  2017   |   11     |    351  |
|  153       |  2017   |   12     |   3000  |
|  153       |  2017   |    1     |  47000  |
|  153       |  2017   |    2     |  35000  |
x------------x---------x---------x----------|




create table #Version (
    Id int 
,   [Name] varchar(100)
,   HistoryTo int 
,   constraint PK_Version primary key (Id)
)
GO

create table #Balance (
    Id int identity(1,1)
,   VersionId int
,   AYear int
,   APer int
,   Amount int
,   constraint PK_Balance primary key (Id)
)
GO

insert #Version
select 0, 'History', 200701 union
select 153, 'Nov 2017', 201711 union
select 154, 'Dec 2017', 201712

insert #Balance
select 0, 2017, 10, 29327 union
select 0, 2017, 11, 351 union
select 0, 2017, 12, 6530 union
select 154, 2018, 1, 25000 union
select 154, 2018, 2, 39136 union
select 154, 2018, 3, 20000 union
select 153, 2017, 12, 3000 union
select 153, 2018, 1, 47000 union
select 153, 2018, 2, 35000 

3 个答案:

答案 0 :(得分:1)

这是一个简单的解决方案:

select v.id, b.Ayear, b.Aper, b.amount
from #Version v join #Balance b on v.historyTo >= (b.Ayear*100+b.Aper)
where v.Id != 0 and b.VersionId = 0
union 
select B.VersionId, B.Ayear, B.Aper, B.amount from #Balance B

你可以在这里测试一下: http://rextester.com/CVEY79303

答案 1 :(得分:0)

我在查询中实现了逻辑,但可以进一步优化。

CREATE TABLE #Version (
    Id INT 
    , [Name] VARCHAR(100)
    , HistoryTo INT 
    , CONSTRAINT PK_Version PRIMARY KEY (Id)
)
GO

CREATE TABLE #Balance (
    Id INT IDENTITY(1,1)
    , VersionId INT
    , AYear INT
    , APer INT
    , Amount INT
    , CONSTRAINT PK_Balance PRIMARY KEY (Id)
)
GO

INSERT #Version
SELECT 0, 'History', 200701 UNION
SELECT 153, 'Nov 2017', 201711 UNION
SELECT 154, 'Dec 2017', 201712

INSERT #Balance
SELECT 0, 2017, 10, 29327 UNION
SELECT 0, 2017, 11, 351 UNION
SELECT 0, 2017, 12, 6530 UNION
SELECT 154, 2018, 1, 25000 UNION
SELECT 154, 2018, 2, 39136 UNION
SELECT 154, 2018, 3, 20000 UNION
SELECT 153, 2017, 12, 3000 UNION
SELECT 153, 2018, 1, 47000 UNION
SELECT 153, 2018, 2, 35000 


SELECT * FROM #Version
SELECT * FROM #Balance


DROP TABLE IF EXISTS #TempBalance
CREATE TABLE #TempBalance (
    Id INT IDENTITY(1,1)
    , VersionId INT
    , AYear INT
    , APer INT
    , Amount INT
    , CONSTRAINT PK_TempBalance PRIMARY KEY (Id)
)
GO


DECLARE @CurrentId INT ;

DECLARE TMP_Cursor CURSOR DYNAMIC FOR 
SELECT Id FROM #Version 

OPEN TMP_Cursor

FETCH NEXT FROM TMP_Cursor INTO @CurrentId

WHILE @@FETCH_STATUS = 0
BEGIN

    INSERT INTO #TempBalance
    SELECT VersionId 
        , AYear
        , APer
        , Amount
    FROM #Balance WHERE VersionId = @CurrentId
    UNION ALL
    SELECT DISTINCT
        @CurrentId AS VersionId 
        , AYear
        , APer
        , Amount
    FROM #TempBalance
    WHERE CONCAT(AYear, APer) NOT IN (SELECT CONCAT(AYear, APer) FROM #Balance WHERE VersionId = @CurrentId)

  FETCH NEXT FROM TMP_Cursor INTO @CurrentId

END

CLOSE TMP_Cursor
DEALLOCATE TMP_Cursor

SELECT 
    VersionId,
    AYear,
    APer,
    Amount 
FROM 
    (
        SELECT 
            * 
            , ROW_NUMBER() OVER(PARTITION BY VersionId, AYear, APer ORDER BY Id DESC) AS Rank1
        FROM #TempBalance
    ) AS T
WHERE 
    Rank1 = 1
ORDER BY 
    VersionId ASC
    , AYear ASC
    , APer ASC

答案 2 :(得分:0)

以下示例了解缺少的内容并添加已经存在的内容。

declare @Version table (Id int primary key, [Name] varchar(100), HistoryTo int);
declare @Balance table (Id int identity(1,1) primary key,   VersionId int,   AYear int,   APer int,   Amount int);

insert @Version (Id, [Name], HistoryTo) values 
(0, 'History', 201701),(153, 'Nov 2017', 201711),(154, 'Dec 2017', 201712);
insert @Balance (VersionId, AYear, APer, Amount) values 
(0,   2017, 10, 29327),(0,   2017, 11,   351),(0,   2017, 12, 6530),
(153, 2017, 12,  3000),(153, 2018,  1, 47000),(153, 2018,  2, 35000),
(154, 2018,  1, 25000),(154, 2018,  2, 39136),(154, 2018,  3, 20000);

select * 
from (
    select bv.VersionId, b0.AYear, b0.APer, b0.Amount
    from (select * from @Balance where VersionId = 0) b0
    cross join (select distinct VersionId from @Balance) bv
    left join @Balance b1 on (b1.AYear = b0.AYear and b1.APer = b0.APer and b1.VersionId = bv.VersionId)
    --join @Version v on (v.Id = bv.VersionId and v.historyTo <= (b0.Ayear*100+b0.Aper))
    where b1.Id is null

    union all

    select b.VersionId, b.AYear, b.APer, b.Amount
    from @Balance b
    join @Version v on (v.Id = b.VersionId and v.historyTo <= (b.Ayear*100+b.Aper))
) q
order by VersionId, AYear, APer, Amount;

结果:

VersionId   AYear   APer    Amount
---------   -----   ----    ------
0           2017    10      29327
0           2017    11      351
0           2017    12      6530
153         2017    10      29327
153         2017    11      351
153         2017    12      3000
153         2018    1       47000
153         2018    2       35000
154         2017    10      29327
154         2017    11      351
154         2017    12      6530
154         2018    1       25000
154         2018    2       39136
154         2018    3       20000