Tại sao số bảng lại là vô giá trị?


112

Chuyên gia cơ sở dữ liệu thường trú của chúng tôi đang nói với chúng tôi rằng các bảng số là vô giá . Tôi không hiểu tại sao. Đây là bảng số:

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

Theo bài đăng trên blog, lý do được đưa ra là

Bảng số thực sự là vô giá. Tôi sử dụng tất cả thời gian để thao tác chuỗi, mô phỏng các chức năng của cửa sổ, điền vào các bảng kiểm tra với nhiều dữ liệu, loại bỏ logic con trỏ và nhiều tác vụ khác sẽ vô cùng khó khăn nếu không có chúng.

Nhưng tôi không hiểu chính xác những sử dụng đó là gì - bạn có thể cung cấp một số ví dụ cụ thể, hấp dẫn về nơi "bảng số" giúp bạn tiết kiệm rất nhiều công việc trong SQL Server - và tại sao chúng ta nên có chúng?


3
Nhiều trường hợp sử dụng cho bảng số có thể được thỏa mãn như nhau bởi CTE đệ quy tạo ra các số bạn cần khi đang di chuyển. Tuy nhiên, có một hình phạt về hiệu suất cũng như một số hạn chế khác đối với phương pháp CTE.
Nick Chammas

4
@Nick: Tôi sẽ nói rằng bảng số dựa trên CTE đang hoạt động so với bảng vật lý chỉ là một chi tiết triển khai về cách bạn tạo bảng số. Khoai tây so với khoai tây ...
Remus Rusanu

1
@Remus - Yup. Tôi chỉ muốn chỉ ra sự thay thế này cho Jeff.
Nick Chammas

2
Tôi có hàng tá câu trả lời bằng cách sử dụng bảng số trên SO stackoverflow.com/search?q=user%3A27535+%2B%22numbers+table%22 .
gbn

Câu trả lời:


82

Tôi đã thấy nhiều cách sử dụng khi bạn cần chiếu 'dữ liệu bị thiếu'. Ví dụ. bạn có một chuỗi thời gian (ví dụ: nhật ký truy cập) và bạn muốn hiển thị số lần truy cập mỗi ngày trong 30 ngày qua (nghĩ bảng điều khiển phân tích). Nếu bạn làm một select count(...) from ... group by daybạn sẽ nhận được số lượng mỗi ngày, nhưng kết quả sẽ chỉ có một hàng cho mỗi ngày bạn thực sự có ít nhất một truy cập. Mặt khác, nếu bạn lần đầu tiên chiếu một bảng ngày từ bảng số của bạn ( select dateadd(day, -number, today) as day from numbers) và sau đó bạn rời khỏi tham gia với số đếm (hoặc áp dụng bên ngoài, bất cứ điều gì bạn thích) thì bạn sẽ nhận được kết quả có 0 cho số ngày bạn không có quyền truy cập. Đây chỉ là một ví dụ. Tất nhiên, người ta có thể lập luận rằng lớp trình bày trong bảng điều khiển của bạn có thể xử lý những ngày bị thiếu và thay vào đó chỉ hiển thị 0, nhưng một số công cụ (ví dụ: SSRS) sẽ không thể xử lý việc này.

Các ví dụ khác tôi đã thấy sử dụng các thủ thuật chuỗi thời gian tương tự (ngày / giờ +/- số) để thực hiện tất cả các loại tính toán cửa sổ. Nói chung, bất cứ khi nào trong một ngôn ngữ bắt buộc, bạn sẽ sử dụng một vòng lặp for với số lần lặp nổi tiếng, tính chất khai báo và thiết lập của SQL có thể sử dụng một mẹo dựa trên bảng số.

BTW, tôi cảm thấy cần phải gọi ra một thực tế rằng mặc dù sử dụng bảng số, nó cảm thấy giống như thực thi thủ tục bắt buộc, không rơi vào sai lầm khi cho rằng nó bắt buộc. Để tôi lấy một ví dụ:

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

Chương trình này sẽ xuất 999999, được đảm bảo khá nhiều.

Hãy thử tương tự trong SQL Server, sử dụng bảng số. Đầu tiên tạo bảng 1.000.000 số:

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

Bây giờ, hãy thực hiện 'for loop':

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

Kết quả là:

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

Nếu bây giờ bạn đang có một khoảnh khắc WTF (sau tất cả number khóa chính được nhóm!), Thủ thuật được gọi là quét thứ tự phân bổ và tôi đã không chèn @j*1000+@ingẫu nhiên ... Bạn cũng có thể mạo hiểm đoán và nói kết quả là vì song song và đó đôi khi có thể là câu trả lời chính xác.

Có rất nhiều troll dưới cây cầu này và tôi đã đề cập một số trong On SQL Server boolean điều hành ngắn mạchT-SQL chức năng làm không bao hàm một trật tự nhất định thực hiện


55

Tôi đã tìm thấy một bảng số khá hữu ích trong nhiều tình huống.

Tại sao tôi nên xem xét sử dụng bảng số phụ? , được viết vào năm 2004, tôi cho thấy một vài ví dụ:

  • Phân tích cú pháp một chuỗi
  • Tìm khoảng cách nhận dạng
  • Tạo phạm vi ngày (ví dụ: điền vào bảng lịch, cũng có thể là vô giá)
  • Tạo các lát cắt thời gian
  • Tạo dải IP

thói quen xấu để đá: sử dụng các vòng lặp để điền vào các bảng lớn , tôi chỉ ra cách sử dụng bảng số để thực hiện công việc ngắn khi chèn nhiều hàng (trái ngược với cách tiếp cận giật đầu gối khi sử dụng vòng lặp while).

Khi xử lý danh sách các số nguyên: cách tiếp cận của tôiThêm về danh sách chia: các dấu phân cách tùy chỉnh, ngăn ngừa trùng lặp và duy trì trật tự , tôi chỉ cách sử dụng bảng số để phân tách một chuỗi (ví dụ: một tập hợp các giá trị được phân tách bằng dấu phẩy) và cung cấp hiệu suất so sánh giữa phương pháp này và phương pháp khác. Thông tin thêm về tách và xử lý chuỗi khác:

Và trong Bảng số máy chủ SQL, Giải thích - Phần 1 , tôi đưa ra một số thông tin cơ bản về khái niệm này và có các bài đăng trong tương lai để lưu trữ các chi tiết ứng dụng cụ thể.

Có nhiều cách sử dụng khác, đó chỉ là một số ít nổi bật với tôi để viết về chúng.

Và giống như @gbn, tôi có một vài câu trả lời về stack overflowtrên trang web này cũng sử dụng bảng số.

Cuối cùng, tôi có một loạt các bài đăng trên blog về việc tạo các bộ mà không lặp, phần nào cho thấy lợi thế về hiệu suất của việc sử dụng bảng số so với hầu hết các phương thức khác (bỏ qua một cách kỳ quặc của Remus):


26

Đây là một ví dụ tuyệt vời mà tôi đã sử dụng gần đây từ 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

Tôi đã sử dụng một cái gì đó tương tự với a CTEđể tìm một trường hợp cụ thể của chuỗi con (nghĩa là "Tìm đường ống thứ 3 trong chuỗi này") để làm việc với dữ liệu được phân tách tương quan:

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

Nếu bạn không có bảng số, cách khác là sử dụng một vòng lặp. Về cơ bản, một bảng số cho phép bạn thực hiện phép lặp dựa trên tập hợp, không có con trỏ hoặc vòng lặp.


5
Và cảnh báo bắt buộc về sự nguy hiểm tiềm ẩn khi thực hiện thao tác chuỗi trong TVF nội tuyến: Các hàm T-SQL không ngụ ý một thứ tự thực hiện nhất định
Remus Rusanu

12

Tôi sẽ sử dụng bảng số bất cứ khi nào tôi cần SQL tương đương với Enumerable.Range. Ví dụ, tôi chỉ sử dụng nó trong một câu trả lời trên trang web này: tính toán số lượng hoán vị

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.