긴 게시물을 사용하지 마십시오. 아래에서 전체 테스트 스크립트를 생성하고 채 웁니다.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
이러한 "메타 - 열/행"접근을 피하는 이유에 대한 훌륭한 게시물을보십시오. http://www.simple-talk.com/sql/database-administration/five-simple--database-design-errors -you-should-avoid/- 체크 아웃 # 3 : 엔티티 속성 값 테이블 –
포인트를 볼 수는 있지만 동의하지는 않습니다. "그렇다면 EAV에서 어떤 이점이 있습니까? 아무도. " 그래도. "메타"접근법이 없다면,이 예제에서 ColumnValues를 저장하기 위해 10 개의 테이블을 생성해야 할 것입니다. 그런 다음 이들 사이의 관계를 저장하기 위해 10 개에서 많은 테이블이 필요합니다. 실제로이 테이블은 멀티 테넌트 데이터베이스이고 Column 테이블에는 클라이언트 당 여러 개의 열을 허용하는 ClientId가 포함되어 있으므로 Create Table/Drop Table 명령의 수가 커질 수 있습니다. –
데이터베이스의 속도를 원하면 데이터베이스가 가장 잘 작동하는 방식으로 작업해야합니다 : 고정 테이블. 다음은 행을 선택하는 비 EAV 쿼리입니다 : SELECT * FROM YourTable Where xyz = ... '이것은 거대한 PIVOT 또는이 것보다 빠를 것입니까? –