Mengapa tabel angka “tidak ternilai”?

112

Pakar database penduduk kami memberi tahu kami bahwa tabel angka sangat berharga . Saya tidak begitu mengerti mengapa. Ini tabel angka:

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

Per posting blog, alasan yang diberikan adalah

Tabel angka benar-benar tak ternilai. Saya menggunakannya sepanjang waktu untuk manipulasi string, mensimulasikan fungsi jendela, mengisi tabel uji dengan banyak data, menghilangkan logika kursor, dan banyak tugas lain yang akan sangat sulit tanpanya.

Tapi saya tidak mengerti apa yang digunakan, tepatnya - dapatkah Anda memberikan beberapa contoh spesifik yang menarik tentang "tabel angka" yang menghemat banyak pekerjaan di SQL Server - dan mengapa kita harus memilikinya?

Jeff Atwood
sumber
3
Banyak kasus penggunaan untuk tabel angka dapat sama-sama dipenuhi oleh CTE rekursif yang menghasilkan angka yang Anda butuhkan dengan cepat. Namun, ada penalti kinerja serta beberapa batasan lain pada pendekatan CTE.
Nick Chammas
4
@Nick: Saya akan mengatakan bahwa tabel nomor CTE berdasarkan-on-the-fly vs tabel fisik hanyalah detail implementasi tentang bagaimana Anda menghasilkan tabel angka. Potato vs. Potato ...
Remus Rusanu
1
@Remus - Yup. Saya hanya ingin menunjukkan alternatif ini kepada Jeff.
Nick Chammas
2
Saya punya selusin jawaban menggunakan tabel angka pada SO stackoverflow.com/search?q=user%3A27535+%2B%22numbers+table%22 .
gbn

Jawaban:

82

Saya telah melihat banyak kegunaan ketika Anda perlu memproyeksikan 'data yang hilang'. Misalnya. Anda memiliki deret waktu (misalnya log akses) dan Anda ingin menunjukkan jumlah klik per hari selama 30 hari terakhir (pikirkan dasbor analitik). Jika Anda melakukan itu, select count(...) from ... group by dayAnda akan mendapatkan hitungan untuk setiap hari, tetapi hasilnya hanya akan memiliki satu baris untuk setiap hari Anda benar-benar memiliki setidaknya satu akses. Di sisi lain jika Anda pertama memproyeksikan tabel hari dari tabel angka Anda ( select dateadd(day, -number, today) as day from numbers) dan kemudian Anda pergi bergabung dengan jumlah (atau menerapkan luar, apa pun yang Anda suka) maka Anda akan mendapatkan hasil yang memiliki 0 untuk perhitungan untuk hari-hari Anda tidak punya akses. Ini hanya satu contoh. Tentu saja, orang mungkin berpendapat bahwa lapisan presentasi dasbor Anda dapat menangani hari-hari yang hilang dan hanya menunjukkan 0 sebagai gantinya, tetapi beberapa alat (mis. SSR) tidak akan bisa menangani ini.

Contoh lain yang pernah saya lihat menggunakan trik seri waktu yang sama (tanggal / waktu +/- nomor) untuk melakukan semua jenis perhitungan jendela. Secara umum, setiap kali dalam bahasa imperatif Anda akan menggunakan for for loop dengan jumlah iterasi yang terkenal, deklaratif dan set sifat SQL dapat menggunakan trik berdasarkan tabel angka.

BTW, saya merasa perlu untuk memanggil fakta bahwa meskipun menggunakan tabel nomor itu terasa seperti eksekusi prosedural penting, jangan jatuh ke dalam kesalahan dari asumsi itu adalah penting. Izinkan saya memberi contoh:

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

Program ini akan menghasilkan 999999, yang dijamin cukup banyak.

Mari kita coba hal yang sama di SQL Server, menggunakan tabel angka. Pertama buat tabel 1.000.000 angka:

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

Sekarang mari kita lakukan 'for loop':

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

Hasilnya adalah:

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

Jika Anda sekarang memiliki momen WTF (setelah semua number adalah kunci utama yang terkelompok!), Triknya disebut pemindaian alokasi alokasi dan saya tidak memasukkannya @j*1000+@isecara tidak sengaja ... Anda juga bisa menebak dan mengatakan hasilnya karena paralelisme dan itu kadang-kadang mungkin jawaban yang benar.

Ada banyak troll di bawah jembatan ini dan saya sebutkan beberapa di hubung singkat pada boolean operator di SQL Server dan fungsi T-SQL tidak menyiratkan urutan eksekusi tertentu

Remus Rusanu
sumber
55

Saya telah menemukan tabel angka yang cukup berguna dalam berbagai situasi.

Di Mengapa saya harus mempertimbangkan menggunakan tabel nomor tambahan? , yang ditulis pada tahun 2004, saya menunjukkan beberapa contoh:

  • Mengurai string
  • Menemukan celah identitas
  • Menghasilkan rentang tanggal (misalnya mengisi tabel kalender, yang juga bisa sangat berharga)
  • Menghasilkan irisan waktu
  • Menghasilkan rentang IP

Pada kebiasaan buruk untuk menendang: menggunakan loop untuk mengisi tabel besar , saya menunjukkan bagaimana tabel angka dapat digunakan untuk membuat pekerjaan singkat memasukkan banyak baris (yang bertentangan dengan pendekatan spontan menggunakan loop sementara).

Di Memproses daftar bilangan bulat: pendekatan saya dan Lainnya di daftar pemisahan: pembatas khusus, mencegah duplikat, dan menjaga ketertiban , saya menunjukkan cara menggunakan tabel angka untuk membagi string (misalnya serangkaian nilai yang dipisahkan koma) dan memberikan kinerja perbandingan antara ini dan metode lainnya. Info lebih lanjut tentang pemisahan dan penanganan string lainnya:

Dan dalam Tabel Bilangan SQL Server, Dijelaskan - Bagian 1 , saya memberikan beberapa latar belakang tentang konsep dan memiliki posting masa depan di toko untuk detail aplikasi spesifik.

Ada banyak kegunaan lain, itu hanya beberapa yang cukup menonjol bagi saya untuk menulis tentang mereka.

Dan seperti @gbn, saya punya beberapa jawaban tentang stack overflow dan di situs ini yang menggunakan tabel angka juga.

Akhirnya, saya memiliki serangkaian posting blog tentang menghasilkan set tanpa perulangan, yang sebagian menunjukkan keunggulan kinerja menggunakan tabel angka dibandingkan dengan sebagian besar metode lainnya (Remus 'outlier yang aneh):

Aaron Bertrand
sumber
26

Berikut adalah contoh bagus yang saya gunakan baru-baru ini dari 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

Saya menggunakan sesuatu yang serupa dengan a CTEuntuk menemukan contoh substring tertentu (yaitu "Temukan pipa ke-3 dalam string ini") untuk bekerja dengan data terbatas berkorelasi:

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

Jika Anda tidak memiliki tabel angka, alternatifnya adalah menggunakan semacam lingkaran. Pada dasarnya, tabel angka memungkinkan Anda melakukan iterasi berbasis set, tanpa kursor atau loop.

JNK
sumber
5
Dan peringatan wajib tentang bahaya mengintai melakukan manipulasi string di TVFs inline: Fungsi T-SQL tidak menyiratkan urutan eksekusi tertentu
Remus Rusanu
12

Saya akan menggunakan tabel angka setiap kali saya membutuhkan SQL setara dengan Enumerable.Range. Sebagai contoh, saya hanya menggunakannya dalam jawaban di situs ini: menghitung jumlah permutasi

AK
sumber