Warum sind Zahlentabellen von unschätzbarem Wert?


112

Unser in der Datenbank ansässiger Experte sagt uns, dass Nummerntabellen von unschätzbarem Wert sind . Ich verstehe nicht ganz warum. Hier ist eine Zahlentabelle:

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

Laut dem Blog-Post lautet die Begründung

Zahlentabellen sind wirklich von unschätzbarem Wert. Ich benutze sie die ganze Zeit, um Zeichenfolgen zu manipulieren, Fensterfunktionen zu simulieren, Testtabellen mit vielen Daten zu füllen, Cursorlogik zu eliminieren und viele andere Aufgaben, die ohne sie unglaublich schwierig wären.

Aber ich verstehe nicht genau, was diese Verwendungen sind - können Sie überzeugende, spezifische Beispiele dafür liefern, wo eine "Zahlentabelle" Ihnen eine Menge Arbeit in SQL Server erspart - und warum wir sie haben sollten?


3
Viele Anwendungsfälle für eine Zahlentabelle können gleichermaßen durch einen rekursiven CTE erfüllt werden, der die Zahlen generiert, die Sie im Handumdrehen benötigen. Es gibt jedoch eine Leistungsbeeinträchtigung sowie einige andere Einschränkungen des CTE-Ansatzes.
Nick Chammas

4
@Nick: Ich würde sagen, dass eine on-the-fly CTE-basierte Zahlentabelle im Vergleich zu einer physischen Tabelle nur ein Implementierungsdetail dafür ist, wie Sie die Zahlentabelle generieren. Kartoffel gegen Kartoffel ...
Remus Rusanu

1
@ Remus - Yup. Ich wollte Jeff nur auf diese Alternative hinweisen.
Nick Chammas

2
Ich habe ein Dutzend Antworten anhand einer Zahlentabelle auf SO stackoverflow.com/search?q=user%3A27535+%2B%22numbers+table%22 .
25.01.12

Antworten:


82

Ich habe viele Anwendungen gesehen, wenn Sie "fehlende Daten" projizieren müssen. Z.B. Sie haben eine Zeitreihe (z. B. ein Zugriffsprotokoll) und möchten die Anzahl der Zugriffe pro Tag in den letzten 30 Tagen anzeigen (Think Analytics Dashboard). Wenn Sie eine select count(...) from ... group by dayausführen, erhalten Sie die Zählung für jeden Tag, aber das Ergebnis enthält nur eine Zeile für jeden Tag, an dem Sie tatsächlich mindestens einen Zugriff hatten. Auf der anderen Seite, wenn Sie zuerst eine Tabelle mit Tagen aus Ihrer Zahlentabelle ( select dateadd(day, -number, today) as day from numbers) projizieren und dann verlassen, um sich den Zählungen anzuschließen (oder äusserlich anzuwenden, was immer Sie möchten), erhalten Sie ein Ergebnis, das für die Tage, die Sie zählen, 0 hat hatte keinen Zugang. Dies ist nur ein Beispiel. Natürlich kann man argumentieren, dass die Präsentationsebene Ihres Dashboards die fehlenden Tage behandeln und stattdessen nur eine 0 anzeigen könnte, aber einige Tools (z. B. SSRS) werden dies einfach nicht können.

Andere Beispiele, die ich gesehen habe, verwendeten ähnliche Zeitreihentricks (Datum / Zeit +/- Zahl), um alle Arten von Fensterberechnungen durchzuführen. Wenn Sie in einer imperativen Sprache eine for-Schleife mit einer bekannten Anzahl von Iterationen verwenden, kann die deklarative und festgelegte Natur von SQL einen Trick verwenden, der auf einer Zahlentabelle basiert.

Übrigens, ich habe das Bedürfnis, darauf hinzuweisen, dass die Verwendung einer Zahlentabelle zwar wie eine zwingende prozedurale Ausführung erscheint, aber nicht in den Irrtum der Annahme fällt, dass sie zwingend ist. Lassen Sie mich ein Beispiel geben:

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

Dieses Programm gibt 999999 aus, das ist so ziemlich garantiert.

Versuchen wir dasselbe in SQL Server unter Verwendung einer Nummerntabelle. Erstellen Sie zunächst eine Tabelle mit 1.000.000 Zahlen:

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*1000+@i);
        set @j += 1;
    end
    commit;
    raiserror (N'Inserted %d*1000', 0, 0, @i)
    begin transaction;
    set @i += 1;
end
commit
go

Nun machen wir die 'for-Schleife':

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

Das Ergebnis ist:

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

Wenn Sie jetzt einen WTF Moment haben (schließlich number ist die Cluster - Primärschlüssel!), Wird der Trick genannt scannen Zuteilung Ordnung und ich habe nicht einfügen @j*1000+@izufällig ... Sie hatte , konnte auch eine Vermutung wagen und sagen , das Ergebnis ist da Parallelität und das kann manchmal die richtige Antwort sein.

Es gibt viele Trolle unter dieser Brücke, und ich habe einige in On SQL Server erwähnte boolesche Operator-Kurzschlüsse erwähnt, und T-SQL-Funktionen implizieren keine bestimmte Ausführungsreihenfolge


55

Ich habe eine Zahlentabelle gefunden, die in verschiedenen Situationen sehr nützlich ist.

Bei Warum sollte ich eine Hilfsnummern Tabelle? , geschrieben im Jahr 2004, zeige ich ein paar Beispiele:

  • Eine Zeichenfolge analysieren
  • Identitätslücken finden
  • Generieren von Datumsbereichen (z. B. Auffüllen einer Kalendertabelle, die auch von unschätzbarem Wert sein kann)
  • Zeitscheiben generieren
  • IP-Bereiche generieren

Bei Bad Habits to Kick: Verwenden von Schleifen zum Auffüllen großer Tabellen zeige ich, wie mit einer Zahlentabelle das Einfügen vieler Zeilen in kurzer Zeit erledigt werden kann (im Gegensatz zum kniffligen Ansatz einer while-Schleife).

Unter Verarbeiten einer Liste von Ganzzahlen: Meine Vorgehensweise und Weitere Informationen zum Teilen von Listen: Benutzerdefinierte Trennzeichen, Verhindern von Duplikaten und Beibehalten der Reihenfolge wird gezeigt, wie mithilfe einer Zahlentabelle eine Zeichenfolge (z. B. eine Reihe von durch Kommas getrennten Werten) geteilt und die Leistung verbessert wird Vergleiche zwischen dieser und anderen Methoden. Weitere Informationen zum Teilen und zum Umgang mit anderen Zeichenfolgen:

Und in der SQL Server-Nummerntabelle (Teil 1) gebe ich Hintergrundinformationen zum Konzept und habe zukünftige Veröffentlichungen vor, in denen bestimmte Anwendungen detailliert beschrieben werden.

Es gibt viele andere Verwendungszwecke, das sind nur einige, die mir so aufgefallen sind, dass ich darüber schreiben kann.

Und wie bei @gbn habe ich ein paar Antworten zum Stapelüberlauf und auf dieser Site , die auch eine Nummerntabelle verwenden.

Schließlich habe ich eine Reihe von Blogposts zum Thema Generieren von Sets ohne Schleifen, die teilweise den Leistungsvorteil der Verwendung einer Zahlentabelle im Vergleich zu den meisten anderen Methoden zeigen (Remus 'skurriler Ausreißer beiseite):


26

Hier ist ein großartiges Beispiel, das ich kürzlich von Adam Machanic verwendet habe:

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

Ich habe etwas Ähnliches mit a verwendet CTE, um eine bestimmte Instanz der Teilzeichenfolge zu finden (z. B. "Finde die dritte Pipe in dieser Zeichenfolge"), um mit korrelierten, abgegrenzten Daten zu arbeiten:

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))=@TargetStr)
SELECT Number
FROM Occurrences
WHERE Occurrence=@Occurrence

Wenn Sie keine Nummerntabelle haben, können Sie auch eine Art Schleife verwenden. Grundsätzlich können Sie mit einer Zahlentabelle eine satzbasierte Iteration ohne Cursor oder Schleifen durchführen.


5
Und die obligatorische Warnung vor der lauernden Gefahr der Manipulation von Strings in Inline-TVFs: T-SQL-Funktionen implizieren keine bestimmte Ausführungsreihenfolge
Remus Rusanu

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.