2010-03-05 4 views
4

긴 게시물을 사용하지 마십시오. 아래에서 전체 테스트 스크립트를 생성하고 채 웁니다.SQL Server 쿼리 성능 - 클러스터 된 인덱스 검색

내 테스트 환경은 다음 표

|--------| |-------------| |-----| |--------------| 
|Column | |ColumnValue | |Row | |RowColumnValue| 
|--------| |-------------| |-----| |--------------| 
|ColumnId| |ColumnValueId| |RowId| |RowId   | 
|Name | |ColumnId  | |Name | |ColumnValueId | 
|--------| |Value  | |-----| |--------------| 
      |-------------| 

그들은 테이블에 행과 열을 나타내는 있습니다. 한 열에있는 셀의 가능한 값은 ColumnValue에 저장됩니다. Row에 대해 선택된 값은 RowColumnValue에 저장됩니다. (나는 분명히 희망한다)

나는 10 개의 열, 10,000 개의 행, 50 개의 컬럼 당 컬럼 값 (500), 25 개의 선택된 컬럼 값 (25 만개)으로 데이터를 채웠다.

필자는 모든 행을 반환하고 열로 피벗되고 각 열에 대해 선택한 열 값의 XML 목록을 포함하는 일부 동적 SQL을가집니다.

참고 : 성능 테스트를 위해 쿼리가 네트워크를 통해 많은 양의 데이터를 반환하지 않도록 SELECT COUNT(*)에 쿼리를 래핑했습니다.

내 테스트 장치는이 쿼리 (카운트 포함)를 약 5-6 초 내에 실행합니다. 실행 계획은 쿼리의 92 %가 클러스터 된 인덱스 탐색에 사용되었음을 보여줍니다. [ColumnValue].[PK_ColumnValue]. 클라이언트 통계는 클라이언트 처리 시간, 총 실행 시간 및 서버 응답 대기 시간을 모두 0으로 표시합니다.

RowColumnValue 테이블의 250KB 행이 상당히 많음을 알고 있으며 SQL Server에서 너무 많이 예상 할 수 있습니다. 그러나 내 기대는 쿼리가 이보다 훨씬 빠르게 실행될 수 있어야한다는 것입니다. 또는 최소한 실행 계획은 Clustered Index Seek가 아닌 다른 병목을 제공해야합니다.

누구든지 문제를 밝힐 수 있습니까? 아니면이 방법을 좀 더 효율적으로 만들 수있는 방법에 대해 제안 해 줄 수 있습니까?

테이블 표시 피벗을 실행

동적 SQL :

DECLARE @columnDataList NVARCHAR(MAX) 
SELECT 
    @columnDataList = 
    CAST 
    (
     (
      SELECT 
       ', CONVERT(xml, [PVT].[' + [Column].[Name] + ']) [Column.' + [Column].[Name] + ']' 
      FROM 
       [Column] 
      ORDER BY 
       [Column].[Name] 
      FOR XML PATH('') 
     ) AS XML 
    ).value('.', 'NVARCHAR(MAX)') 

DECLARE @columnPivotList NVARCHAR(MAX) 
SELECT 
    @columnPivotList = 
    CAST 
    (
     (
      SELECT 
       ', [' + [Column].[Name] + ']' 
      FROM 
       [Column] 
      ORDER BY 
       [Column].[Name] 
      FOR XML PATH('') 
     ) AS XML 
    ).value('.', 'NVARCHAR(MAX)') 

EXEC(' 
    SELECT 
     COUNT(*) 
    FROM 
    (
     SELECT 
      [PVT].[RowId] 
      ' + @columnDataList + ' 
     FROM 
     (
      SELECT 
       [Row].[RowId], 
       [Column].[Name] [ColumnName], 
       [XmlRowColumnValues].[XmlRowColumnValues] [XmlRowColumnValues] 
      FROM 
       [Row] 
      CROSS JOIN 
       [Column] 
      CROSS APPLY 
      (
       SELECT 
        [ColumnValue].[Value] [Value] 
       FROM 
        [RowColumnValue] 
       INNER JOIN 
        [ColumnValue] 
       ON 
        [ColumnValue].[ColumnValueId] = [RowColumnValue].[ColumnValueId] 
       WHERE 
        [RowColumnValue].[RowId] = [Row].[RowId] 
       AND 
        [ColumnValue].[ColumnId] = [Column].[ColumnId] 
       FOR XML PATH (''''), ROOT(''Values'') 
      ) [XmlRowColumnValues] ([XmlRowColumnValues]) 
     ) [PivotData] 
     PIVOT 
     (
      MAX([PivotData].[XmlRowColumnValues]) 
     FOR 
      [ColumnName] 
      IN 
      ([0]' + @columnPivotList + ') 
     ) PVT 
    ) RowColumnData 
') 

스크립트를 생성하고 채울 데이터베이스 :

SET ANSI_NULLS ON 
GO 
SET QUOTED_IDENTIFIER ON 
GO 
CREATE TABLE [dbo].[Row](
    [RowId] [int] IDENTITY(1,1) NOT NULL, 
    [Name] [nvarchar](50) NOT NULL, 
CONSTRAINT [PK_Row] PRIMARY KEY CLUSTERED 
(
    [RowId] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 
GO 
SET ANSI_NULLS ON 
GO 
SET QUOTED_IDENTIFIER ON 
GO 
CREATE TABLE [dbo].[Column](
    [ColumnId] [int] IDENTITY(1,1) NOT NULL, 
    [Name] [nvarchar](50) NOT NULL, 
CONSTRAINT [PK_Column] PRIMARY KEY CLUSTERED 
(
    [ColumnId] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 
GO 
SET ANSI_NULLS ON 
GO 
SET QUOTED_IDENTIFIER ON 
GO 
CREATE TABLE [dbo].[RowColumnValue](
    [RowId] [int] NOT NULL, 
    [ColumnValueId] [int] NOT NULL, 
CONSTRAINT [PK_RowColumnValue] PRIMARY KEY CLUSTERED 
(
    [RowId] ASC, 
    [ColumnValueId] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 
GO 
SET ANSI_NULLS ON 
GO 
SET QUOTED_IDENTIFIER ON 
GO 
CREATE TABLE [dbo].[ColumnValue](
    [ColumnValueId] [int] IDENTITY(1,1) NOT NULL, 
    [ColumnId] [int] NOT NULL, 
    [Value] [nvarchar](50) NOT NULL, 
CONSTRAINT [PK_ColumnValue] PRIMARY KEY CLUSTERED 
(
    [ColumnValueId] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 
GO 
CREATE NONCLUSTERED INDEX [FKIX_ColumnValue_ColumnId] ON [dbo].[ColumnValue] 
(
    [ColumnId] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
GO 
ALTER TABLE [dbo].[ColumnValue] WITH CHECK ADD CONSTRAINT [FK_ColumnValue_Column] FOREIGN KEY([ColumnId]) 
REFERENCES [dbo].[Column] ([ColumnId]) 
GO 
ALTER TABLE [dbo].[ColumnValue] CHECK CONSTRAINT [FK_ColumnValue_Column] 
GO 
ALTER TABLE [dbo].[RowColumnValue] WITH CHECK ADD CONSTRAINT [FK_RowColumnValue_ColumnValue] FOREIGN KEY([ColumnValueId]) 
REFERENCES [dbo].[ColumnValue] ([ColumnValueId]) 
GO 
ALTER TABLE [dbo].[RowColumnValue] CHECK CONSTRAINT [FK_RowColumnValue_ColumnValue] 
GO 
ALTER TABLE [dbo].[RowColumnValue] WITH CHECK ADD CONSTRAINT [FK_RowColumnValue_Row] FOREIGN KEY([RowId]) 
REFERENCES [dbo].[Row] ([RowId]) 
GO 
ALTER TABLE [dbo].[RowColumnValue] CHECK CONSTRAINT [FK_RowColumnValue_Row] 
GO 

DECLARE @columnLoop INT 
DECLARE @columnValueLoop INT 
DECLARE @rowLoop INT 

DECLARE @columnId INT 
DECLARE @columnValueId INT 
DECLARE @rowId INT 

SET @columnLoop = 0 

WHILE @columnLoop < 10 
BEGIN 

    INSERT INTO [Column] ([Name]) VALUES(NEWID()) 

    SET @columnId = @@IDENTITY 

    SET @columnValueLoop = 0 

    WHILE @columnValueLoop < 50 
    BEGIN 

     INSERT INTO [ColumnValue] ([ColumnId], [Value]) VALUES(@columnId, NEWID()) 

     SET @columnValueLoop = @columnValueLoop + 1 

    END 

    SET @columnLoop = @columnLoop + 1 

END 

SET @rowLoop = 0 

WHILE @rowLoop < 10000 
BEGIN 

    INSERT INTO [Row] ([Name]) VALUES(NEWID()) 

    SET @rowId = @@IDENTITY 

    INSERT INTO [RowColumnValue] ([RowId], [ColumnValueId]) SELECT TOP 25 @rowId, [ColumnValueId] FROM [ColumnValue] ORDER BY NEWID() 

    SET @rowLoop = @rowLoop + 1 

END 
+7

이러한 "메타 - 열/행"접근을 피하는 이유에 대한 훌륭한 게시물을보십시오. http://www.simple-talk.com/sql/database-administration/five-simple--database-design-errors -you-should-avoid/- 체크 아웃 # 3 : 엔티티 속성 값 테이블 –

+0

포인트를 볼 수는 있지만 동의하지는 않습니다. "그렇다면 EAV에서 어떤 이점이 있습니까? 아무도. " 그래도. "메타"접근법이 없다면,이 예제에서 ColumnValues를 저장하기 위해 10 개의 테이블을 생성해야 할 것입니다. 그런 다음 이들 사이의 관계를 저장하기 위해 10 개에서 많은 테이블이 필요합니다. 실제로이 테이블은 멀티 테넌트 데이터베이스이고 Column 테이블에는 클라이언트 당 여러 개의 열을 허용하는 ClientId가 포함되어 있으므로 Create Table/Drop Table 명령의 수가 커질 수 있습니다. –

+1

데이터베이스의 속도를 원하면 데이터베이스가 가장 잘 작동하는 방식으로 작업해야합니다 : 고정 테이블. 다음은 행을 선택하는 비 EAV 쿼리입니다 : SELECT * FROM YourTable Where xyz = ... '이것은 거대한 PIVOT 또는이 것보다 빠를 것입니까? –

답변

2

나는이 그 @marc_s 및 @KM에 동의 웅대 한 디자인은 처음부터 끝장입니다.

수백만 명의 Microsoft 개발자 시간이 강력하고 강력한 데이터베이스 엔진을 구축하고 미세 조정했지만 많은 수의 일반 테이블에 모든 것을 넣은 다음 모든 것을 다시 구현할 것입니다. SQL Server는 이미 사용자를 위해 설계되었습니다.

SQL Server에는 이미 엔터티 이름, 열 이름 등이 포함 된 테이블이 있습니다. 일반적으로 이러한 시스템 테이블과 직접 상호 작용하지 않는다는 사실은 좋은 것입니다. 추상화라고합니다. SQL Server보다 추상화를 더 잘 구현할 가능성은 거의 없습니다.

당신의 접근 방식으로, (a) 가장 단순한 쿼리조차도 괴물이 될 것입니다; (b) 그렇지 않으면 무료로 얻을 수있는 모든 쿼리 최적화를 거치지 않으므로 최적의 성능에 근접하지 않을 것입니다.

응용 프로그램이나 요구 사항에 대해 더 이상 알지 못하는 경우 특정 조언을 제공하는 것이 어렵습니다. 그러나 나는 오래된 좋은 정규화가 먼 길을 가도록 제안 할 것이다. 잘 구현되고 평범하지 않은 데이터베이스에는 많은 테이블이 있습니다. 10 개의 테이블과 10 개의 테이블이 당신을 깜짝 놀라게하지 않아야합니다.

서로 다른 테이블에서 공통 인터페이스를 구현하는 방법으로 SQL 코드 생성을 두려워하지 마십시오. 조금은 먼 길을 갈 수 있습니다.

+0

우선 테이블을 저장할 데이터베이스 엔진을 만들려고합니다. 행/열 테이블 이름은 테스트 하네스를 쉽게 만들고 문제의 쿼리에 대해 반환 할 방법을 보여주기위한 것입니다. 실제 테이블은 데이터를 분류하는 방법으로, ColumnValue의 태그가있는 태그 클라우드와 비슷하며 Column 테이블 인 추가 "태그 유형"이 있습니다. 태그 된 항목은 행 테이블입니다. 나는이 실제 개념을 질문에 넣고 누군가가 더 나은 구현을 내놓을 수 있는지 확인하기 위해 시간을 할애 할 것이다. –

+0

그걸 추가하려면 "Column"테이블이 "10x"가 아닐 것입니다, 이것은 단지이 예제에서였습니다. 현재 "Column"테이블에는 ~ 3000 개의 항목이 있으며 각 항목에는 2-3 개의 유효한 항목에서 최대 수백 개의 항목까지 유효한 항목 "ColumnValue"가 필요합니다. 각 고객은 2-3 가지 특정 유형의 태깅부터 20 가지 방식으로 태그를 분류하는 태깅까지 모든 것을 갖추고 있습니다. –

+0

@Robin - 죄송합니다. 세부 정보를 볼 수 있기를 기대합니다. 여기에 의견을 추가하여 개정 된 게시물 (또는 그렇게하도록 선택하면 새로운 질문)을 보도록하겠습니다. –

관련 문제