Sqlserver
 sql >> Teknologi Basis Data >  >> RDS >> Sqlserver

Pemisahan Persimpangan Rentang Tanggal dalam SQL

Masalah yang akan Anda hadapi dengan masalah ini adalah seiring bertambahnya kumpulan data, solusi untuk menyelesaikannya dengan TSQL tidak akan berskala dengan baik. Di bawah ini menggunakan serangkaian tabel sementara yang dibuat dengan cepat untuk menyelesaikan masalah. Ini membagi setiap entri rentang tanggal ke hari masing-masing menggunakan tabel angka. Di sinilah ia tidak akan menskala, terutama karena nilai NULL rentang terbuka Anda yang tampaknya tak terhingga, jadi Anda harus menukar tanggal tetap jauh ke masa depan yang membatasi rentang konversi ke jangka waktu yang layak. Anda mungkin dapat melihat kinerja yang lebih baik dengan membuat tabel hari atau tabel kalender dengan pengindeksan yang sesuai untuk perenderan yang dioptimalkan setiap hari.

Setelah rentang dipisah, deskripsi digabungkan menggunakan XML PATH sehingga setiap hari dalam rangkaian rentang memiliki semua deskripsi yang tercantum untuknya. Penomoran Baris oleh PersonID dan Tanggal memungkinkan baris pertama dan terakhir dari setiap rentang ditemukan menggunakan dua pemeriksaan NOT EXISTS untuk menemukan contoh di mana baris sebelumnya tidak ada untuk kumpulan PersonID dan Deskripsi yang cocok, atau di mana baris berikutnya tidak ada. t ada untuk kumpulan PersonID dan Deskripsi yang cocok.

Kumpulan hasil ini kemudian dinomori ulang menggunakan ROW_NUMBER sehingga dapat dipasangkan untuk membangun hasil akhir.

/*
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

*/

Solusi di atas juga akan menangani kesenjangan antara Deskripsi tambahan, jadi jika Anda ingin menambahkan Deskripsi lain untuk PersonID 18 tinggalkan celah:

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

Ini akan mengisi celah dengan tepat. Seperti yang ditunjukkan dalam komentar, Anda seharusnya tidak memiliki informasi nama di tabel ini, itu harus dinormalisasi ke Tabel Orang yang dapat DIGABUNGKAN ke dalam hasil akhir. Saya mensimulasikan tabel lain ini dengan menggunakan SELECT DISTINCT untuk membuat tabel temp untuk membuat GABUNG itu.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Batasan multiplisitas melanggar SQL Server 2008 - CodeFirst

  2. Kesalahan sqlserver_ado bukan backend basis data yang tersedia (PyISAPIe di IIS)

  3. Gunakan tipe data waktu SQL Server dalam aplikasi C#.NET?

  4. SQL Dinamis dengan Loop Over Semua Kolom dalam Tabel

  5. Tabel SQL Server Dinamakan Dengan Skema dbo