sql >> Databáze >  >> RDS >> Sqlserver

Rozdělení průniku datového rozsahu v SQL

Problém, který s tímto problémem budete mít, je ten, že jak roste datová sada, řešení pro její vyřešení pomocí TSQL se nebudou dobře škálovat. Níže uvedený postup používá k vyřešení problému řadu dočasných tabulek vytvořených za běhu. Rozdělí každý záznam období na příslušné dny pomocí tabulky čísel. Zde se nebude škálovat, primárně kvůli vašim otevřeným hodnotám NULL, které se zdají být nekonečno, takže musíte vyměnit v pevné datum daleko do budoucnosti, které omezuje rozsah převodu na proveditelnou dobu. Pravděpodobně byste mohli dosáhnout lepšího výkonu vytvořením tabulky dnů nebo kalendářní tabulky s vhodným indexováním pro optimalizované vykreslování každého dne.

Jakmile jsou rozsahy rozděleny, jsou popisy sloučeny pomocí XML PATH, takže každý den v řadě rozsahů má všechny popisy uvedené pro něj. Číslování řádků podle PersonID a Date umožňuje najít první a poslední řádek každého rozsahu pomocí dvou kontrol NEEXISTUJE k nalezení případů, kde předchozí řádek neexistuje pro odpovídající sadu PersonID a Description, nebo kde další řádek neexistuje. Pro odpovídající sadu PersonID a Description neexistuje.

Tato sada výsledků je poté přečíslována pomocí ROW_NUMBER, aby bylo možné je spárovat a vytvořit tak konečné výsledky.

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

*/

Výše uvedené řešení si také poradí s mezerami mezi dalšími popisy, takže pokud byste přidali další popis pro PersonID 18 a nechali mezeru:

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

Vhodně vyplní mezeru. Jak bylo zdůrazněno v komentářích, v této tabulce byste neměli mít informace o jménu, měla by být normalizována na tabulku osob, ke které lze v konečném výsledku JOIN'd. Tuto další tabulku jsem simuloval pomocí SELECT DISTINCT k vytvoření dočasné tabulky pro vytvoření tohoto JOIN.



  1. Odstraňte klauzuli limitu z MySQL Workbench

  2. MySQL je pomalé při připojení. Jakýkoli způsob, jak urychlit

  3. Jak předat proměnné SSIS ve výrazu SQLCommand ODBC?

  4. Jak zachovat řád dětí, aby se objevily po rodičích