Sqlserver
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Sqlserver

Tách giao điểm phạm vi ngày trong SQL

Vấn đề bạn sẽ gặp phải với vấn đề này là khi tập dữ liệu phát triển, các giải pháp để giải quyết nó với TSQL sẽ không mở rộng quy mô tốt. Dưới đây sử dụng một loạt các bảng tạm thời được xây dựng để giải quyết vấn đề. Nó chia từng mục nhập phạm vi ngày thành các ngày tương ứng bằng cách sử dụng một bảng số. Đây là nơi nó sẽ không mở rộng quy mô, chủ yếu do các giá trị NULL trong phạm vi mở của bạn dường như là vô cùng, vì vậy bạn phải hoán đổi vào một ngày cố định trong tương lai, giới hạn phạm vi chuyển đổi trong một khoảng thời gian khả thi. Bạn có thể thấy hiệu suất tốt hơn bằng cách tạo bảng ngày hoặc bảng lịch với lập chỉ mục thích hợp để hiển thị tối ưu hóa từng ngày.

Khi các phạm vi được tách ra, các mô tả được hợp nhất bằng cách sử dụng XML PATH để mỗi ngày trong chuỗi phạm vi có tất cả các mô tả được liệt kê cho nó. Đánh số hàng theo PersonID và Ngày tháng cho phép tìm thấy hàng đầu tiên và hàng cuối cùng của mỗi phạm vi bằng cách sử dụng hai lần kiểm tra KHÔNG TỒN TẠI để tìm các trường hợp mà hàng trước đó không tồn tại cho tập hợp PersonID và Mô tả phù hợp hoặc khi hàng tiếp theo không tồn tại ' không tồn tại cho một PersonID và mô tả phù hợp.

Tập kết quả này sau đó được đánh số lại bằng ROW_NUMBER để chúng có thể được ghép nối với nhau để tạo ra kết quả cuối cùng.

/*
SET DATEFORMAT dmy
USE tempdb;
GO
CREATE TABLE Schedule
( PersonID int, 
 Surname nvarchar(30), 
 FirstName nvarchar(30), 
 Description nvarchar(100), 
 StartDate datetime, 
 EndDate datetime)
GO
INSERT INTO Schedule VALUES (18, 'Smith', 'John', 'Poker Club', '01/01/2009', NULL)
INSERT INTO Schedule VALUES (18, 'Smith', 'John', 'Library', '05/01/2009', '18/01/2009')
INSERT INTO Schedule VALUES (18, 'Smith', 'John', 'Gym', '10/01/2009', '28/01/2009')
INSERT INTO Schedule VALUES (26, 'Adams', 'Jane', 'Pilates', '03/01/2009', '16/02/2009')
GO

*/

SELECT 
 PersonID, 
 Description, 
 theDate
INTO #SplitRanges
FROM Schedule, (SELECT DATEADD(dd, number, '01/01/2008') AS theDate
    FROM master..spt_values
    WHERE type = N'P') AS DayTab
WHERE theDate >= StartDate 
  AND theDate <= isnull(EndDate, '31/12/2012')

SELECT 
 ROW_NUMBER() OVER (ORDER BY PersonID, theDate) AS rowid,
 PersonID, 
 theDate, 
 STUFF((
  SELECT '/' + Description
  FROM #SplitRanges AS s
  WHERE s.PersonID = sr.PersonID 
    AND s.theDate = sr.theDate
  FOR XML PATH('')
  ), 1, 1,'') AS Descriptions
INTO #MergedDescriptions
FROM #SplitRanges AS sr
GROUP BY PersonID, theDate


SELECT 
 ROW_NUMBER() OVER (ORDER BY PersonID, theDate) AS ID, 
 *
INTO #InterimResults
FROM
(
 SELECT * 
 FROM #MergedDescriptions AS t1
 WHERE NOT EXISTS 
  (SELECT 1 
   FROM #MergedDescriptions AS t2 
   WHERE t1.PersonID = t2.PersonID 
     AND t1.RowID - 1 = t2.RowID 
     AND t1.Descriptions = t2.Descriptions)
UNION ALL
 SELECT * 
 FROM #MergedDescriptions AS t1
 WHERE NOT EXISTS 
  (SELECT 1 
   FROM #MergedDescriptions AS t2 
   WHERE t1.PersonID = t2.PersonID 
     AND t1.RowID = t2.RowID - 1
     AND t1.Descriptions = t2.Descriptions)
) AS t

SELECT DISTINCT 
 PersonID, 
 Surname, 
 FirstName
INTO #DistinctPerson
FROM Schedule

SELECT 
 t1.PersonID, 
 dp.Surname, 
 dp.FirstName, 
 t1.Descriptions, 
 t1.theDate AS StartDate, 
 CASE 
  WHEN t2.theDate = '31/12/2012' THEN NULL 
  ELSE t2.theDate 
 END AS EndDate
FROM #DistinctPerson AS dp
JOIN #InterimResults AS t1 
 ON t1.PersonID = dp.PersonID
JOIN #InterimResults AS t2 
 ON t2.PersonID = t1.PersonID 
  AND t1.ID + 1 = t2.ID 
  AND t1.Descriptions = t2.Descriptions

DROP TABLE #SplitRanges
DROP TABLE #MergedDescriptions
DROP TABLE #DistinctPerson
DROP TABLE #InterimResults

/*

DROP TABLE Schedule

*/

Giải pháp trên cũng sẽ xử lý khoảng trống giữa các Mô tả bổ sung, vì vậy nếu bạn thêm một Mô tả khác cho PersonID 18 sẽ để lại khoảng trống:

INSERT INTO Schedule VALUES (18, 'Smith', 'John', 'Gym', '10/02/2009', '28/02/2009')

Nó sẽ lấp đầy khoảng trống một cách thích hợp. Như đã chỉ ra trong các nhận xét, bạn không nên có thông tin tên trong bảng này, nó phải được chuẩn hóa thành Bảng Người có thể được THAM GIA trong kết quả cuối cùng. Tôi đã mô phỏng bảng khác này bằng cách sử dụng CHỌN DISTINCT để tạo bảng tạm thời để tạo THAM GIA đó.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Tôi cần nhập dữ liệu từ Excel sang SQL Server bằng VBA

  2. Làm cách nào để giám sát các câu lệnh sql đã thực thi trên SQL Server 2005

  3. Giới thiệu về SQL Server 2017

  4. Viết CTE đệ quy bằng cách sử dụng Entity Framework Cú pháp thông thạo hoặc cú pháp nội tuyến

  5. SQL Server heap v.s. chỉ mục nhóm