我有一个包含四列的表:MeterName,TenantLease,BuildingID和Date(月的最后一天)。在一起查看所有四列时,每行都是唯一的。
问题:
如果在2016年11月30日至2017年10月31日之间没有一个月的条目,那么我的查询结果应该为每个MeterName标识 缺少 日期条目,TenantLease和BuildingID。
例1: 对于Meter1,Tenant1,bld1,我希望返回9/30/2017和10/31/2017(以及所有列 - MeterName,TenantLease和Buildingid),因为这些是缺少的条目。
例2: 对于Meter2,Tenant1,bld1,我预计将于2016年11月30日,2016年12月31日,2017年4月30日,2016年8月31日,9月30日和2017年10月31日(以及所有columns - MeterName,TenantLease和Buildingid)因为这些是缺少的条目。
我尝试了完全加入,交叉应用,外部应用等等。但是,没有人会返回我需要的数据。
答案 0 :(得分:1)
这有几个步骤,很抱歉很长的回复。
首先,这是我用来测试它的表变量。
DECLARE @tbl TABLE (
MeterName NVARCHAR(MAX),
TenantLease NVARCHAR(MAX),
BuildingID NVARCHAR(MAX),
date DATE
)
INSERT @tbl VALUES
('Meter1','Tenant1','bld1','2016-11-30'),('Meter1','Tenant1','bld1','2016-12-31'),
('Meter1','Tenant1','bld1','2017-01-31'),('Meter1','Tenant1','bld1','2017-02-28'),
('Meter1','Tenant1','bld1','2017-03-31'),('Meter1','Tenant1','bld1','2017-04-30'),
('Meter1','Tenant1','bld1','2017-05-31'),('Meter1','Tenant1','bld1','2017-06-30'),
('Meter1','Tenant1','bld1','2017-07-31'),('Meter1','Tenant1','bld1','2017-08-31'),
('Meter2','Tenant1','bld1','2017-01-31'),('Meter2','Tenant1','bld1','2017-02-28'),
('Meter2','Tenant1','bld1','2017-03-31'),('Meter2','Tenant1','bld1','2017-05-31'),
('Meter2','Tenant1','bld1','2017-06-30'),('Meter2','Tenant1','bld1','2017-07-31'),
('Meter1','Tenant2','bld2','2017-01-31'),('Meter1','Tenant2','bld2','2017-02-28'),
('Meter1','Tenant2','bld2','2017-03-31'),('Meter1','Tenant2','bld2','2017-05-31'),
('Meter1','Tenant2','bld2','2017-06-30'),('Meter1','Tenant2','bld2','2017-07-31')
接下来,我们需要创建一个要加入的日期表,以提供您正在寻找的完整表格。
DECLARE @startdate DATE = '2016-11-01'
DECLARE @enddate DATE = '2017-10-31'
DECLARE @dates TABLE (
dates DATE
)
WHILE @startdate <= @enddate
BEGIN
INSERT @dates
VALUES (DATEADD(DAY,-1,DATEADD(MONTH,1,@startdate)))
SET @startdate = DATEADD(MONTH,1,@startdate)
END
最后,我们通过获取MeterName,TenantLease,BuildingID以及您要查找的所有日期的笛卡尔结果集来创建您要查找的完整结果集。我们加入您的表格,查找缺少表格且存在笛卡尔结果的记录。这些是你遗失的记录。
SELECT
tbl2.MeterName,
tbl2.TenantLease,
tbl2.BuildingID,
tbl2.dates AS missing_date
FROM @tbl tbl
FULL OUTER JOIN (
SELECT DISTINCT
tbl.MeterName,
tbl.TenantLease,
tbl.BuildingID,
dates.dates
FROM @tbl tbl
FULL OUTER JOIN @dates dates ON
1 = 1 --This is always true, resulting in a Cartesion result.
)tbl2 ON
tbl.MeterName = tbl2.MeterName
AND tbl.TenantLease = tbl2.TenantLease
AND tbl.BuildingID = tbl2.BuildingID
AND tbl.date = tbl2.dates
WHERE tbl.MeterName IS NULL
答案 1 :(得分:1)
这是一个计数表或函数派上用场的情况之一。 请注意,此解决方案仅在表中进行一次传递,并且使用正确的索引可以消除由LEAD函数引起的排序操作。
IF OBJECT_ID('tempdb..#MeterData', 'U') IS NOT NULL
DROP TABLE #MeterData;
CREATE TABLE #MeterData (
MeterName CHAR(6) NOT NULL,
TenantLease CHAR(7) NOT NULL,
BuildingID CHAR(4) NOT NULL,
mDate DATE NOT NULL
);
INSERT #MeterData(MeterName, TenantLease, BuildingID, mDate) VALUES
('Meter1','Tenant1','bld1','2016-11-30'),
('Meter1','Tenant1','bld1','2016-12-31'),
('Meter1','Tenant1','bld1','2017-01-31'),
('Meter1','Tenant1','bld1','2017-02-28'),
('Meter1','Tenant1','bld1','2017-03-31'),
('Meter1','Tenant1','bld1','2017-04-30'),
('Meter1','Tenant1','bld1','2017-05-31'),
('Meter1','Tenant1','bld1','2017-06-30'),
('Meter1','Tenant1','bld1','2017-07-31'),
('Meter1','Tenant1','bld1','2017-08-31'),
('Meter2','Tenant1','bld1','2017-01-31'),
('Meter2','Tenant1','bld1','2017-02-28'),
('Meter2','Tenant1','bld1','2017-03-31'),
('Meter2','Tenant1','bld1','2017-05-31'),
('Meter2','Tenant1','bld1','2017-06-30'),
('Meter2','Tenant1','bld1','2017-07-31'),
('Meter1','Tenant2','bld2','2017-01-31'),
('Meter1','Tenant2','bld2','2017-02-28'),
('Meter1','Tenant2','bld2','2017-03-31'),
('Meter1','Tenant2','bld2','2017-05-31'),
('Meter1','Tenant2','bld2','2017-06-30'),
('Meter1','Tenant2','bld2','2017-07-31');
-- add a non-clustered "POC" index to eliminate the sort operation caused by the LEAD function
CREATE NONCLUSTERED INDEX ix_MeterName_TenantLease_BuildingID_mDate ON #MeterData (MeterName, TenantLease, BuildingID, mDate);
-- SELECT * FROM #MeterData md ORDER BY md.MeterName, md.TenantLease, md.BuildingID, md.mDate;
--=============================================================================================
-- The actual solution
WITH
cte_MonthGap AS (
SELECT
md.MeterName, md.TenantLease, md.BuildingID, md.mDate,
FirstOfMonth = DATEFROMPARTS(YEAR(md.mDate), MONTH(md.mDate), 1),
MonthGap = DATEDIFF(MONTH, md.mDate, LEAD(md.mDate, 1, '2017-11-01') OVER (PARTITION BY md.MeterName, md.TenantLease, md.BuildingID ORDER BY md.mDate)) - 1
FROM
#MeterData md
)
SELECT
mg.MeterName, mg.TenantLease, mg.BuildingID,
MissingMonth = EOMONTH(DATEADD(MONTH, t.n, mg.FirstOfMonth))
FROM
cte_MonthGap mg
CROSS APPLY dbo.tfn_Tally(mg.MonthGap, 1) t;
结果...
MeterName TenantLease BuildingID MissingMonth
--------- ----------- ---------- ------------
Meter1 Tenant1 bld1 2017-09-30
Meter1 Tenant1 bld1 2017-10-31
Meter1 Tenant2 bld2 2017-04-30
Meter1 Tenant2 bld2 2017-08-31
Meter1 Tenant2 bld2 2017-09-30
Meter1 Tenant2 bld2 2017-10-31
Meter2 Tenant1 bld1 2017-04-30
Meter2 Tenant1 bld1 2017-08-31
Meter2 Tenant1 bld1 2017-09-30
Meter2 Tenant1 bld1 2017-10-31
dbo.tfn_Tally的代码......
CREATE FUNCTION dbo.tfn_Tally
/* ==================================================================================================
Capable of creating a sequense of rows ranging from -10,000,000,000,000,000 to 10,000,000,000,000,000
================================================================================================== */
(
@NumOfRows BIGINT,
@StartWith BIGINT
)
RETURNS TABLE WITH SCHEMABINDING AS
RETURN
WITH
cte_n1 (n) AS (SELECT 1 FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n (n)), -- 10 rows
cte_n2 (n) AS (SELECT 1 FROM cte_n1 a CROSS JOIN cte_n1 b), -- 100 rows
cte_n3 (n) AS (SELECT 1 FROM cte_n2 a CROSS JOIN cte_n2 b), -- 10,000 rows
cte_n4 (n) AS (SELECT 1 FROM cte_n3 a CROSS JOIN cte_n3 b), -- 100,000,000 rows
cte_Tally (n) AS (
SELECT TOP (@NumOfRows)
(ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1) + @StartWith
FROM
cte_n4 a CROSS JOIN cte_n4 b -- 10,000,000,000,000,000 rows
)
SELECT
t.n
FROM
cte_Tally t;
GO
答案 2 :(得分:1)
我们的想法是将完整的租户列表与实际列表中的所有日期进行比较,并使用EXCEPT语句显示差异。
注意:为了使用简单的计数表,我使用了STRING_SPLIT函数,该函数仅适用于MSSQL 2016,否则您可以使用UDF计数函数。
DECLARE @STARTDATE DATE='2016-11-30'
DECLARE @ENDDATE DATE='2017-10-31'
DECLARE @NOOFMONTHS INT=DATEDIFF(MONTH, @STARTDATE, @ENDDATE)
DECLARE @Tenants_Original TABLE (METERNAME VARCHAR(20), TENANTLEASE VARCHAR(20), BUILDINGID VARCHAR(20), [DATE] DATE)
INSERT @Tenants_Original VALUES
('METER1','TENANT1','BLD1','2016-11-30'),
('METER1','TENANT1','BLD1','2016-12-31'),
('METER1','TENANT1','BLD1','2017-01-31'),
('METER1','TENANT1','BLD1','2017-02-28'),
('METER1','TENANT1','BLD1','2017-03-31'),
('METER1','TENANT1','BLD1','2017-04-30'),
('METER1','TENANT1','BLD1','2017-05-31'),
('METER1','TENANT1','BLD1','2017-06-30'),
('METER1','TENANT1','BLD1','2017-07-31'),
('METER1','TENANT1','BLD1','2017-08-31'),
('METER2','TENANT1','BLD1','2017-01-31'),
('METER2','TENANT1','BLD1','2017-02-28'),
('METER2','TENANT1','BLD1','2017-03-31'),
('METER2','TENANT1','BLD1','2017-05-31'),
('METER2','TENANT1','BLD1','2017-06-30'),
('METER2','TENANT1','BLD1','2017-07-31'),
('METER1','TENANT2','BLD2','2017-01-31'),
('METER1','TENANT2','BLD2','2017-02-28'),
('METER1','TENANT2','BLD2','2017-03-31'),
('METER1','TENANT2','BLD2','2017-05-31'),
('METER1','TENANT2','BLD2','2017-06-30'),
('METER1','TENANT2','BLD2','2017-07-31');
WITH MYDATES AS(
SELECT EOMONTH( DATEADD(MONTH,-1+ ROW_NUMBER()
OVER(ORDER BY (SELECT NULL)), @STARTDATE)) AS [DATE]
FROM STRING_SPLIT( REPLICATE('1,', @NOOFMONTHS), ',')
), Tenants_Full AS(
SELECT DISTINCT A.METERNAME, A.TENANTLEASE, A.BUILDINGID, B.[DATE]
FROM @Tenants_Original AS A
CROSS APPLY MYDATES AS B
)
SELECT * FROM Tenants_Full
EXCEPT
SELECT * FROM @Tenants_Original
ORDER BY 1,2,3,4