it-swarm-eu.dev

Proč jsou číselné tabulky „neocenitelné“?

Náš rezident odborník na databázi nám říká, že tabulky čísel jsou neocenitelné . Nerozumím proč. Zde je tabulka čísel:

USE Model
GO

CREATE TABLE Numbers
(
    Number INT NOT NULL,
    CONSTRAINT PK_Numbers 
        PRIMARY KEY CLUSTERED (Number)
        WITH FILLFACTOR = 100
)

INSERT INTO Numbers
SELECT
    (a.Number * 256) + b.Number AS Number
FROM 
    (
        SELECT number
        FROM master..spt_values
        WHERE 
            type = 'P'
            AND number <= 255
    ) a (Number),
    (
        SELECT number
        FROM master..spt_values
        WHERE 
            type = 'P'
            AND number <= 255
    ) b (Number)
GO

U příspěvku na blogu je uvedeno zdůvodnění

Číselné tabulky jsou opravdu neocenitelné. Po celou dobu je používám k manipulaci s řetězci, simulaci funkcí okna, naplnění testovacích tabulek množstvím dat, eliminaci logiky kurzoru a mnoha dalších úkolů, které by bez nich byly neuvěřitelně obtížné.

Ale nerozumím tomu, co tato použití jsou, přesně - můžete uvést některé přesvědčivé, konkrétní příklady, kde vám „tabulka čísel“ ušetří spoustu práce na serveru SQL - a proč bychom je měli mít?

112
Jeff Atwood

Viděl jsem mnoho použití, když potřebujete promítat „chybějící data“. Např. máte časovou řadu (například přístupový protokol) a chcete zobrazit počet požadavků na server za den za posledních 30 dní (hlavní panel analytické analýzy). Pokud uděláte funkci select count(...) from ... group by day, dostanete počet za každý den, ale výsledek bude mít pouze řádek pro každý den, kdy jste skutečně měli alespoň jeden přístup. Na druhou stranu, pokud nejprve promítáte tabulku dnů z tabulky čísel (select dateadd(day, -number, today) as day from numbers) a poté se necháte spojit s počty (nebo vnějšími aplikacemi, ať už si chcete libovolně vybrat), získáte výsledek, který má 0 pro počet dní, kdy jste neměli přístup. Toto je jen jeden příklad. Samozřejmě lze tvrdit, že prezentační vrstva vašeho dashboardu by mohla zvládnout chybějící dny a namísto toho zobrazit pouze 0, ale některé nástroje (např. SSRS) to prostě nebudou schopny zvládnout.

Jiné příklady, které jsem viděl, používaly podobné triky časových řad (datum/čas +/- číslo) k provádění všech druhů výpočtů oken. Obecně, kdykoli v imperativním jazyce, který byste použili pro smyčku s dobře známým počtem iterací, může deklarativní a nastavená povaha SQL použít trik založený na tabulce čísel.

BTW, cítím potřebu upozornit na skutečnost, že i když používám tabulku čísel, se cítí jako imperativní procedurální poprava, nespadá do omylu za předpokladu, že to bude je nezbytná. Dovolte mi uvést příklad:

int x;
for (int i=0;i<1000000;++i)
  x = i;
printf("%d",x);

Tento program poskytne výstup 999999, což je do značné míry zaručeno.

Zkusme to samé na serveru SQL pomocí tabulky čísel. Nejprve vytvořte tabulku 1 000 000 čísel:

create table numbers (number int not null primary key);
go

declare @i int = 0
    , @j int = 0;

set nocount on;
begin transaction
while @i < 1000
begin
    set @j = 0;
    while @j < 1000
    begin
        insert into numbers (number) 
            values (@j*[email protected]);
        set @j += 1;
    end
    commit;
    raiserror (N'Inserted %d*1000', 0, 0, @i)
    begin transaction;
    set @i += 1;
end
commit
go

Nyní můžete provést „for loop“:

declare @x int;
select @x = number 
from numbers with(nolock);
select @x as [@x];

Výsledek je:

@x
-----------
88698

Pokud nyní máte WTF moment (po tom všem, že number je seskupený primární klíč!), Trik se nazývá - skenování objednávek a nevložil jsem @j*[email protected] náhodou ... Mohli byste se také pustit do hádání a říci, že výsledek je proto, že rovnoběžnost a někdy může být správnou odpovědí.

Existuje mnoho trollové pod tímto mostem a některé jsem zmínil v Na SQL Serveru boolean operátor zkrat) a T-SQL funkce neimplikují určité pořadí provedení

82
Remus Rusanu

Našel jsem číselnou tabulku docela užitečnou v různých situacích.

Na Proč bych měl zvážit použití tabulky pomocných čísel? , napsané v roce 2004, ukážu několik příkladů:

  • Analýza řetězce
  • Hledání mezer v identitě
  • Generování časových období (např. Naplnění tabulky kalendáře, která může být také neocenitelná)
  • Vytváření časových řezů
  • Generování rozsahů IP

Na Špatné návyky na kop: pomocí smyček k naplnění velkých tabulek , ukážu, jak lze pomocí tabulky čísel provést krátkou práci s vložením mnoha řádků (na rozdíl od přístupu kolenního škubání pomocí chvíli smyčka).

Na Zpracování seznamu celých čísel: můj přístup a Více o seznamech rozdělení: vlastní oddělovače, zamezení duplikátů a udržování pořadí , ukážu, jak použít tabulku čísel k rozdělení řetězec (např. soubor hodnot oddělených čárkami) a poskytují srovnání výkonu mezi touto a jinými metodami. Další informace o dělení a manipulaci s řetězci:

A v Tabulka čísel serveru SQL, vysvětlil - část 1 , dávám nějaké pozadí o konceptu a mít budoucí příspěvky v obchodě podrobně konkrétní aplikace.

Existuje mnoho dalších použití, to je jen několik, které se mi postavily natolik, aby o nich psaly.

A stejně jako @ gbn, mám několik odpovědí na přetečení zásobník a na tomto web , které používají také tabulku čísel.

Nakonec mám řadu blogových příspěvků o generování sad bez opakování, což částečně ukazuje výkonovou výhodu použití tabulky čísel ve srovnání s většinou ostatních metod (Remusův nepředvídatelný okraj):

56
Aaron Bertrand

Zde je skvělý příklad, který jsem nedávno použil od Adam Machanic:

CREATE FUNCTION dbo.GetSubstringCount
(
    @InputString TEXT, 
    @SubString VARCHAR(200),
    @NoisePattern VARCHAR(20)
)
RETURNS INT
WITH SCHEMABINDING
AS
BEGIN
    RETURN 
    (
        SELECT COUNT(*)
        FROM dbo.Numbers N
        WHERE
            SUBSTRING(@InputString, N.Number, LEN(@SubString)) = @SubString
            AND PATINDEX(@NoisePattern, SUBSTRING(@InputString, N.Number + LEN(@SubString), 1)) = 0
            AND 0 = 
                CASE 
                    WHEN @NoisePattern = '' THEN 0
                    ELSE PATINDEX(@NoisePattern, SUBSTRING(@InputString, N.Number - 1, 1))
                END
    )
END

Pomocí CTE jsem použil něco podobného k nalezení konkrétní instance podřetězce (tj. „Najít 3. potrubí v tomto řetězci“) pro práci s korelovanými ohraničenými daty:

declare @TargetStr varchar(8000), 
@SearchedStr varchar(8000), 
@Occurrence int
set @TargetStr='a'
set @SearchedStr='abbabba'
set @Occurrence=3;

WITH Occurrences AS (
SELECT Number,
       ROW_NUMBER() OVER(ORDER BY Number) AS Occurrence
FROM master.dbo.spt_values
WHERE Number BETWEEN 1 AND LEN(@SearchedStr) AND type='P'
  AND SUBSTRING(@SearchedStr,Number,LEN(@TargetStr))[email protected])
SELECT Number
FROM Occurrences
WHERE [email protected]

Pokud nemáte tabulku čísel, je alternativou použít smyčku nějakého druhu. Tabulka čísel v zásadě umožňuje provádět iteraci založenou na sadě, bez kurzorů nebo smyček.

26
JNK

Použil bych číselnou tabulku, kdykoli potřebuji SQL ekvivalent Enumerable.Range. Například jsem ji použil v odpovědi na tomto webu: výpočet počtu permutací

12
A-K