숫자 표가“가치가없는”이유는 무엇입니까?


112

우리의 상주 데이터베이스 전문가숫자 테이블이 매우 중요 하다고 말합니다 . 왜 그런지 잘 모르겠습니다. 숫자 표는 다음과 같습니다.

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

블로그 게시물에 따르면 주어진 근거는

숫자 테이블은 정말 귀중합니다. 문자열 조작, 창 함수 시뮬레이션, 많은 양의 데이터로 테스트 테이블 채우기, 커서 논리 제거 및 그 없이는 매우 어려운 많은 다른 작업에 항상 사용합니다.

그러나 그 용도가 무엇인지 정확히 이해하지 못합니다. "숫자 테이블"이 SQL Server에서 많은 작업을 절약 할 수있는 매력적이고 구체적인 예를 제공 할 수 있습니까?


3
숫자 테이블의 많은 사용 사례는 필요한 숫자를 즉시 ​​생성하는 재귀 CTE로 동일하게 충족 될 수 있습니다. 그러나 CTE 접근 방식에 대한 다른 제한 사항뿐만 아니라 성능 저하도 있습니다 .
Nick Chammas 2012 년

4
@ Nick : 온더 플라이 CTE 기반 숫자 테이블과 물리적 테이블은 숫자 테이블을 생성하는 방법에 대한 구현 세부 사항 일뿐 입니다. 감자 대 감자 ...
Remus Rusanu

1
@ 레무스-예. 나는이 대안을 Jeff에게 지적하고 싶었다.
Nick Chammas 2012 년

2
SO stackoverflow.com/search?q=user%3A27535+%2B%22numbers+table%22 에서 숫자 표를 사용하여 12 개의 답변이 있습니다.
gbn

답변:


82

'결측 데이터'를 투영해야 할 때 많은 용도를 보았습니다. 예 : 시계열 (예 : 액세스 로그)이 있고 지난 30 일 동안 매일 히트 수를 표시하려고합니다 (분석 대시 보드 생각). 당신이 그렇게하면 select count(...) from ... group by day매일 카운트를 얻을 수 있지만 결과는 실제로 적어도 하나의 액세스 권한을 가진 매일 매일 행을 갖게됩니다. 반면에 숫자 표 ( select dateadd(day, -number, today) as day from numbers) 에서 일표를 투영 한 다음 카운트와 조인 (또는 외부 적용, 공상이있는 경우)을 남겨두면 요일 수에 대한 결과가 0이됩니다. 접근 할 수 없었습니다. 이것은 하나의 예일뿐입니다. 물론 대시 보드의 프레젠테이션 계층이 누락 된 일을 처리하고 대신 0을 표시 할 수 있다고 주장 할 수 있지만 일부 도구 (예 : SSRS)는이를 처리 할 수 ​​없습니다.

내가 본 다른 예제는 모든 종류의 창 계산을 위해 유사한 시계열 트릭 (날짜 / 시간 +/- 숫자)을 사용했습니다. 일반적으로 명령형 언어로 잘 알려진 반복 횟수와 함께 for 루프를 사용하면 SQL의 선언적이고 설정된 특성이 숫자 테이블을 기반으로하는 트릭을 사용할 수 있습니다.

BTW, 나는 숫자 테이블을 사용하는 것이 필수적 절차 적 실행처럼 느껴지 더라도 그것이 필수적이라는 가정의 잘못에 빠지지 않는다는 사실을 외면 할 필요 있다고 생각합니다 . 예를 들어 보겠습니다.

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

이 프로그램은 999999를 출력 할 것입니다.

숫자 테이블을 사용하여 SQL Server에서 동일하게 시도 할 수 있습니다. 먼저 1,000,000 개의 숫자로 구성된 테이블을 만듭니다.

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

이제 'for loop'를 해봅시다 :

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

결과는 다음과 같습니다.

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

(모든 후 지금 WTF 모멘트가 발생하는 경우 number 입니다 클러스터 된 기본 키!), 트릭가 호출 할당하기 위해 스캔 내가 삽입하지 않은 @j*1000+@i사고로 ... 또한 가지고 추측 벤처와 결과가 있기 때문이다 말할 수 병렬성 과 때로는 정답 일 수 있습니다.

이 브리지 에는 많은 트롤이 있으며 On SQL Server 부울 연산자 단락 에서 일부를 언급 했으며 T-SQL 함수는 특정 실행 순서를 암시하지 않습니다.


55

다양한 상황에서 숫자 표가 매우 유용하다는 것을 알았습니다.

이유는 보조 번호 테이블을 사용하는 것이 좋습니다? 2004 년에 작성된 몇 가지 예를 보여줍니다.

  • 문자열 파싱
  • 정체성 격차 찾기
  • 날짜 범위 생성 (예 : 달력 테이블 채우기)
  • 타임 슬라이스 생성
  • IP 범위 생성

에서 걷어차 나쁜 습관 : 대형 테이블을 채우기 위해 루프를 사용하여 , 나는 숫자 테이블 (while 루프를 사용하는 반사적 인 접근 방식에 반대) 행를 많이 삽입하는 간단한 작업을하는 데 사용할 수있는 방법을 보여줍니다.

에서 정수의 목록을 처리 : 내 접근 방식분리 목록에 대한 자세한 : 사용자 정의 구분 기호, 중복을 방지하고, 질서를 유지 , 내가 문자열을 분할 할 수 테이블을 사용하는 방법을 보여줍니다 (예 : 쉼표로 구분 된 값의 집합)와 성능을 제공 이 방법과 다른 방법의 비교. 분리 및 기타 문자열 처리에 대한 자세한 정보 :

그리고 SQL Server Numbers Table, Explained-Part 1 에서는 개념에 대한 배경 지식을 제공하고 특정 응용 프로그램을 자세히 설명하기 위해 향후 게시물을 저장합니다.

다른 많은 용도가 있습니다. 그것들에 대해 쓸만큼 충분히 눈에 띄는 몇 가지 있습니다.

@gbn과 마찬가지로 스택 오버플로 와 숫자 테이블을 사용하는 이 사이트에도 몇 가지 답변 이 있습니다 .

마지막으로 루핑없이 세트를 생성하는 방법에 대한 일련의 블로그 게시물이 있습니다.


26

다음은 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

CTE상관 된 구분 된 데이터로 작업하기 위해 하위 문자열의 특정 인스턴스 (예 : "이 문자열에서 세 번째 파이프 찾기")를 찾기 위해 a 와 비슷한 것을 사용했습니다 .

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

만약 당신이 숫자 테이블이 없다면, 대안은 어떤 종류의 루프를 사용하는 것입니다. 기본적으로 숫자 테이블을 사용하면 커서 나 루프없이 세트 기반 반복을 수행 할 수 있습니다.


5
인라인 TVF에서 문자열 조작을 수행 할 때 발생하는 위험에 대한 필수 경고 : T-SQL 함수는 특정 실행 순서를 암시하지 않습니다
Remus Rusanu

12

Enumerable.Range와 동등한 SQL이 필요할 때마다 숫자 테이블을 사용합니다. 예를 들어, 나는이 사이트의 답변에 그것을 사용했습니다 : 순열 수 계산

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.