5つのテーブルからデータを取得するビューを作成しました。そのうち3つは200k +行、340k +行、2.1mill +行を含みます。
ビューには、group by句、caseステートメント、およびCTEが含まれます。
単純な1000行の選択またはビューから*を選択すると、上位1000行を取得するだけで約15分という長い時間がかかります。
これは、Excelファイルにインポートしようとしたときにも発生し、50分以上かかります。それをスピードアップする方法について何か考えはありますか?
これがビューのスクリプトです
CREATE VIEW [iot].[Occupancy_View]
AS
WITH CTE1
AS
(
SELECT A.[TpID], A.[RoomCode], A.[RoomName], A.[BuildingCode],A.[SchoolCode],A.[SchoolCode1], b.[DeviceID], b.[LocalTime], b.[Occupancy], b.[Temperature],c.[semesterid],c.[academicyear]
FROM iot.Device A
inner JOIN iot.DeviceMessageHistory b ON A.[DeviceID] = b.[DeviceID]
LEFT OUTER JOIN iot.SEMESTER c ON b.[LocalTime] BETWEEN c.[semesterstartDATE] and c.[semesterendDATE]
),
CTE2
AS
(
SELECT
ro.[RoomCode],
ro.[RoomName],
ro.[BuildingCode],
ro.[SchoolCode],
ro.[SchoolCode1],
ro.[DeviceID],
ro.[TpID],
ro.[semesterid],
ro.[academicyear],
CAST(avg(ro.[Temperature]) AS decimal(10, 1)) AS [Temperature],
CASE
WHEN Ceiling(AVG(CAST(ro.Occupancy AS DECIMAL))) BETWEEN 0.1 AND 1.0
THEN '1'
ELSE
'0'
END AS [Occupancy],
DATEADD(ss,-1,
DATEADD(HOUR, DATEPART(HOUR, ro.[LocalTime]), DATEADD( MINUTE, 30 * CAST((DATEDIFF(MINUTE, '19000101', ro.[LocalTime]) / 30) % 2 AS INT),
CAST(CAST(ro.[LocalTime] AS DATE) AS DATETIME)))
)
AS [Time],
(DATEPART(day,ro.[LocalTime])-1)/7 + 1 AS [WEEKNO],
rbt.[booker] AS Booker,
coalesce((ctt.[duration]/60),rbt.[duration]) as Duration,
coalesce(ctt.[Day],rbt.[day]) AS [Day],
coalesce(ctt.[CLASSSTARTDATE], rbt.[startdatetime]) AS [StartDateTime],
coalesce(ctt.[CLASSENDDATE], rbt.[enddatetime]) AS [EndDateTime]
FROM
CTE1 ro
LEFT OUTER JOIN
[iot].[ClassTimeTable] ctt ON ro.RoomCode = ctt.ROOMID AND (ro.LocalTime BETWEEN ctt.CLASSSTARTDATE AND ctt.CLASSENDDATE)
LEFT OUTER JOIN
[iot].[RoomBooking] rbt ON ro.RoomCode = rbt.facilityname and (ro.LocalTime BETWEEN rbt.startdatetime AND rbt.enddatetime)
GROUP BY
ro.[RoomCode],
ro.[RoomName],
ro.[BuildingCode],
ro.[SchoolCode],
ro.[SchoolCode1],
ro.[DeviceID],
ro.[TpID],
ro.[semesterid],
ro.[academicyear],
DATEADD(HOUR, DATEPART(HOUR, ro.[LocalTime]), DATEADD( MINUTE, 30 * CAST((DATEDIFF(MINUTE, '19000101', ro.[LocalTime]) / 30) % 2 AS INT),
CAST(CAST(ro.[LocalTime] AS DATE) AS DATETIME))),
(DATEPART(day,ro.[LocalTime])-1)/7 + 1,
rbt.[booker],
coalesce((ctt.[duration]/60),rbt.[duration]) ,
coalesce(ctt.[Day],rbt.[day]),
coalesce(ctt.[CLASSSTARTDATE], rbt.[startdatetime]) ,
coalesce(ctt.[CLASSENDDATE], rbt.[enddatetime])
),
CTE3
AS
(
SELECT *
FROM CTE2
CROSS APPLY
(
SELECT
CASE
WHEN (CTE2.Time BETWEEN CTE2.StartDateTime AND CTE2.EndDateTime) AND
(CAST(CTE2.[Occupancy]AS DECIMAL)) > 0.0
THEN 'Booked / Occupied'
WHEN (CTE2.Time BETWEEN CTE2.StartDateTime AND CTE2.EndDateTime) AND
(CAST(CTE2.[Occupancy]AS DECIMAL)) <= 0.0
THEN 'Booked / Unoccupied'
WHEN (CTE2.Time NOT BETWEEN CTE2.StartDateTime AND CTE2.EndDateTime) OR ((CTE2.SEMESTERID IS NULL ) or (CTE2.Booker IS NULL)) AND
(CAST(CTE2.[Occupancy]AS DECIMAL)) > 0.0
THEN 'Not Booked / Occupied'
WHEN (CTE2.Time NOT BETWEEN CTE2.StartDateTime AND CTE2.EndDateTime) OR ((CTE2.SEMESTERID IS NULL) or (CTE2.Booker IS NULL)) AND
(CAST(CTE2.[Occupancy]AS DECIMAL)) <= 0.0
THEN 'Not Booked / Unoccupied'
--(Add another conditon)
ELSE
'ERROR'
END AS ClassroomStatus
) AS CS
)
SELECT * FROM CTE3;
GO
次に実行計画を示します。 https://imgur.com/a/cFPSk3M
情報が限られている場合、最も簡単な解決策は、ストアドプロシージャを使用して、CTEを一時テーブルに分割することです。
CTEは具体化されていないため、ビューは実際には1つの大きな複雑なクエリです。
クエリが大きくなると、最適化が難しくなります。それを分割すると、オプティマイザは最適化にいくらかの余地を与えます。
2つの一時テーブルの例(テーブルと列を定義し、INSERT INTO
)。
CREATE PROCEDURE [iot].[Occupancy_View]
AS
SELECT A.[TpID], A.[RoomCode], A.[RoomName], A.[BuildingCode],A.[SchoolCode],A.[SchoolCode1], b.[DeviceID], b.[LocalTime], b.[Occupancy], b.[Temperature],c.[semesterid],c.[academicyear]
INTO #TEMP1
FROM iot.Device A
inner JOIN iot.DeviceMessageHistory b ON A.[DeviceID] = b.[DeviceID]
LEFT OUTER JOIN iot.SEMESTER c ON b.[LocalTime] BETWEEN c.[semesterstartDATE] and c.[semesterendDATE]
SELECT
ro.[RoomCode],
ro.[RoomName],
ro.[BuildingCode],
ro.[SchoolCode],
ro.[SchoolCode1],
ro.[DeviceID],
ro.[TpID],
ro.[semesterid],
ro.[academicyear],
CAST(avg(ro.[Temperature]) AS decimal(10, 1)) AS [Temperature],
CASE
WHEN Ceiling(AVG(CAST(ro.Occupancy AS DECIMAL))) BETWEEN 0.1 AND 1.0
THEN '1'
ELSE
'0'
END AS [Occupancy],
DATEADD(ss,-1,
DATEADD(HOUR, DATEPART(HOUR, ro.[LocalTime]), DATEADD( MINUTE, 30 * CAST((DATEDIFF(MINUTE, '19000101', ro.[LocalTime]) / 30) % 2 AS INT),
CAST(CAST(ro.[LocalTime] AS DATE) AS DATETIME)))
)
AS [Time],
(DATEPART(day,ro.[LocalTime])-1)/7 + 1 AS [WEEKNO],
rbt.[booker] AS Booker,
coalesce((ctt.[duration]/60),rbt.[duration]) as Duration,
coalesce(ctt.[Day],rbt.[day]) AS [Day],
coalesce(ctt.[CLASSSTARTDATE], rbt.[startdatetime]) AS [StartDateTime],
coalesce(ctt.[CLASSENDDATE], rbt.[enddatetime]) AS [EndDateTime]
INTO #TEMP2
FROM
#TEMP1 ro
LEFT OUTER JOIN
[iot].[ClassTimeTable] ctt ON ro.RoomCode = ctt.ROOMID AND (ro.LocalTime BETWEEN ctt.CLASSSTARTDATE AND ctt.CLASSENDDATE)
LEFT OUTER JOIN
[iot].[RoomBooking] rbt ON ro.RoomCode = rbt.facilityname and (ro.LocalTime BETWEEN rbt.startdatetime AND rbt.enddatetime)
GROUP BY
ro.[RoomCode],
ro.[RoomName],
ro.[BuildingCode],
ro.[SchoolCode],
ro.[SchoolCode1],
ro.[DeviceID],
ro.[TpID],
ro.[semesterid],
ro.[academicyear],
DATEADD(HOUR, DATEPART(HOUR, ro.[LocalTime]), DATEADD( MINUTE, 30 * CAST((DATEDIFF(MINUTE, '19000101', ro.[LocalTime]) / 30) % 2 AS INT),
CAST(CAST(ro.[LocalTime] AS DATE) AS DATETIME))),
(DATEPART(day,ro.[LocalTime])-1)/7 + 1,
rbt.[booker],
coalesce((ctt.[duration]/60),rbt.[duration]) ,
coalesce(ctt.[Day],rbt.[day]),
coalesce(ctt.[CLASSSTARTDATE], rbt.[startdatetime]) ,
coalesce(ctt.[CLASSENDDATE], rbt.[enddatetime])
SELECT *
FROM #TEMP2 TEMP2
CROSS APPLY
(
SELECT
CASE
WHEN (TEMP2.Time BETWEEN TEMP2.StartDateTime AND TEMP2.EndDateTime) AND
(CAST(TEMP2.[Occupancy]AS DECIMAL)) > 0.0
THEN 'Booked / Occupied'
WHEN (TEMP2.Time BETWEEN TEMP2.StartDateTime AND TEMP2.EndDateTime) AND
(CAST(TEMP2.[Occupancy]AS DECIMAL)) <= 0.0
THEN 'Booked / Unoccupied'
WHEN (TEMP2.Time NOT BETWEEN TEMP2.StartDateTime AND TEMP2.EndDateTime) OR ((TEMP2.SEMESTERID IS NULL ) or (TEMP2.Booker IS NULL)) AND
(CAST(TEMP2.[Occupancy]AS DECIMAL)) > 0.0
THEN 'Not Booked / Occupied'
WHEN (TEMP2.Time NOT BETWEEN TEMP2.StartDateTime AND TEMP2.EndDateTime) OR ((TEMP2.SEMESTERID IS NULL) or (TEMP2.Booker IS NULL)) AND
(CAST(TEMP2.[Occupancy]AS DECIMAL)) <= 0.0
THEN 'Not Booked / Unoccupied'
--(Add another conditon)
ELSE
'ERROR'
END AS ClassroomStatus
) AS CS