คุณรู้วิธีง่ายๆในการสร้างหนึ่งบันทึกในแต่ละชั่วโมงของ 12 ชั่วโมงที่ผ่านมา?


12

ฉันมีรายงานที่แสดงจำนวนเหตุการณ์ใน 12 ชั่วโมงที่ผ่านมาจัดกลุ่มตามชั่วโมง ฟังดูง่ายพอ แต่สิ่งที่ฉันดิ้นรนคือทำอย่างไรจึงจะรวมระเบียนที่ครอบคลุมช่องว่าง

นี่คือตารางตัวอย่าง:

Event
(
  EventTime datetime,
  EventType int
)

ข้อมูลมีลักษณะดังนี้:

  '2012-03-08 08:00:04', 1
  '2012-03-08 09:10:00', 2
  '2012-03-08 09:11:04', 2
  '2012-03-08 09:10:09', 1
  '2012-03-08 10:00:17', 4
  '2012-03-08 11:00:04', 1

ฉันต้องสร้างชุดผลลัพธ์ที่มีหนึ่งระเบียนสำหรับทุก ๆ ชั่วโมงของ 12 ชั่วโมงที่ผ่านมาไม่ว่าจะมีเหตุการณ์ในช่วงเวลานั้นหรือไม่ก็ตาม

สมมติว่าเวลาปัจจุบันคือ '2012-03-08 11:00:00' รายงานจะแสดง (คร่าวๆ):

Hour  EventCount
----  ----------
23    0
0     0
1     0
2     0
3     0
4     0
5     0
6     0
7     0
8     1
9     3
10    1

ฉันคิดวิธีแก้ปัญหาที่ใช้ตารางที่มีหนึ่งระเบียนสำหรับทุกชั่วโมงของวัน ฉันจัดการเพื่อให้ได้ผลลัพธ์ที่ต้องการโดยใช้ UNION และตรรกะบางอย่างที่ซับซ้อนในประโยคที่ แต่ฉันหวังว่าจะมีใครบางคนมีทางออกที่ดีกว่า

คำตอบ:


21

สำหรับ SQL Server 2005+ คุณสามารถสร้าง 12 ระเบียนเหล่านั้นได้อย่างง่ายดายด้วยลูปและ CTE แบบเรียกซ้ำ นี่คือตัวอย่างของ CTE แบบเรียกซ้ำ:

DECLARE @Date DATETIME
SELECT @Date = '20120308 11:00:00'

;WITH Dates AS
(
    SELECT DATEPART(HOUR,DATEADD(HOUR,-1,@Date)) [Hour], 
      DATEADD(HOUR,-1,@Date) [Date], 1 Num
    UNION ALL
    SELECT DATEPART(HOUR,DATEADD(HOUR,-1,[Date])), 
      DATEADD(HOUR,-1,[Date]), Num+1
    FROM Dates
    WHERE Num <= 11
)
SELECT [Hour], [Date]
FROM Dates

จากนั้นคุณเพิ่งเข้าร่วมกับตารางกิจกรรมของคุณ


2
ฉันพบสิ่งนี้หลังจากที่คุณโพสต์ explextended.com/2009/10/21/ ...... มันบ่งชี้ว่าการใช้ CTE สำหรับวัตถุประสงค์นี้มีประสิทธิภาพน้อยกว่าตารางที่จัดเก็บ มันเป็นเรื่องจริงเหรอ? อย่างที่ Nick พูดมันอาจจะไม่สำคัญกับคดีนี้ แต่ ...
Leigh Riffel

4
ฉันคิดว่ามันจะสร้างความแตกต่างด้วยจำนวนแถวที่มากขึ้นถ้าคุณต้องการ 12 ระเบียนแล้วจะไม่มีการแสดงที่ยอดเยี่ยมเลย
Lamak

Lamak และ @swasheck อืม ... ฉันมาช้าไปหน่อย (ติดตามหัวข้อนี้ไม่ได้) ในการไปถึง แต่ไม่มีปัญหา ดูคำตอบที่ฉันโพสต์ในที่สุดเพื่อสนับสนุนการเรียกร้องของฉันข้างต้น และจำไว้ว่ารหัสทั้งหมดมีผลสะสม หากทุกคนที่เขียนโค้ดนั้น "เร็วขึ้น" 16 ครั้งเร็วขึ้นครึ่งหนึ่งของโพสต์บนฟอรัมเช่นนี้จะไม่จำเป็นอีกต่อไป และใช้เวลาไม่นาน (บางครั้งก็สั้นกว่า) ในการเขียนโค้ดได้เร็วขึ้น
Jeff Moden

10

Tally table สามารถใช้กับสิ่งต่าง ๆ เช่นนี้ พวกเขาสามารถมีประสิทธิภาพมาก สร้างตารางการนับด้านล่าง ฉันสร้างตารางรวมที่มีเพียง 24 แถวสำหรับตัวอย่างของคุณ แต่คุณสามารถสร้างได้ด้วยจำนวนที่คุณต้องการให้เหมาะกับวัตถุประสงค์อื่น

SELECT TOP 24 
        IDENTITY(INT,1,1) AS N
   INTO dbo.Tally
   FROM Master.dbo.SysColumns sc1,
        Master.dbo.SysColumns sc2

--===== Add a Primary Key to maximize performance
  ALTER TABLE dbo.Tally
    ADD CONSTRAINT PK_Tally_N 
        PRIMARY KEY CLUSTERED (N) WITH FILLFACTOR = 100

ฉันคิดว่าตารางของคุณถูกเรียกว่า dbo.tblEvents เรียกใช้แบบสอบถามด้านล่าง ฉันเชื่อว่านี่คือสิ่งที่คุณกำลังมองหา:

SELECT t.n, count(e.EventTime)
FROM dbo.Tally t
LEFT JOIN dbo.tblEvent e  on t.n = datepart(hh, e.EventTime)
GROUP BY t.n
ORDER BY t.n

ฉันเชื่อว่าเครดิตไปที่ลิงก์ต่อไปนี้ฉันเชื่อว่านี่เป็นที่ที่ฉันได้พบสิ่งนี้เป็นครั้งแรก:

http://www.sqlservercentral.com/articles/T-SQL/62867/

http://www.sqlservercentral.com/articles/T-SQL/74118/


+1 แต่ความหมายมันเป็นตารางของตัวเลขไม่ใช่ตารางของการนับ
Aaron Bertrand

1
หนึ่งในคำจำกัดความของ "Tally" คือ "To Count" "Tally Table" ตั้งชื่อตาม "Tally Stick" ซึ่งเป็นแท่งที่ยาวและผอมซึ่งถูกใช้ในการนับ
Jeff Moden

7

ก่อนอื่นฉันขอโทษสำหรับความล่าช้าในการตอบสนองของฉันตั้งแต่ความคิดเห็นล่าสุดของฉัน

หัวเรื่องปรากฏขึ้นในความคิดเห็นที่ใช้ Recursive CTE (rCTE จากที่นี่เป็นต้นไป) วิ่งเร็วพอเนื่องจากมีจำนวนแถวน้อย แม้ว่าอาจดูเหมือนเป็นอย่างนั้น แต่ก็ไม่มีอะไรเพิ่มเติมจากความจริง

สร้างตารางที่สูงและฟังก์ชั่นที่ยอดเยี่ยม

ก่อนที่เราจะเริ่มการทดสอบเราจำเป็นต้องสร้างตาราง Tally แบบกายภาพด้วยดัชนี Clustered ที่เหมาะสมและฟังก์ชัน Tally ของสไตล์ Itzik Ben-Gan เราจะทำสิ่งนี้ทั้งหมดใน TempDB เพื่อที่เราจะได้ไม่ทิ้งสารพัดของทุกคนโดยไม่ตั้งใจ

นี่คือรหัสในการสร้าง Tally Table และเวอร์ชันการผลิตปัจจุบันของฉันในรหัสที่ยอดเยี่ยมของ Itzik

--===== Do this in a nice, safe place that everyone has
    USE tempdb
;
--===== Create/Recreate a Physical Tally Table
     IF OBJECT_ID('dbo.Tally','U') IS NOT NULL
        DROP TABLE dbo.Tally
;
     -- Note that the ISNULL makes a NOT NULL column
 SELECT TOP 1000001
        N = ISNULL(ROW_NUMBER() OVER (ORDER BY (SELECT NULL))-1,0)
   INTO dbo.Tally
   FROM      sys.all_columns ac1
  CROSS JOIN sys.all_columns ac2
;
  ALTER TABLE dbo.Tally
    ADD CONSTRAINT PK_Tally PRIMARY KEY CLUSTERED (N)
;
--===== Create/Recreate a Tally Function
     IF OBJECT_ID('dbo.fnTally','IF') IS NOT NULL
        DROP FUNCTION dbo.fnTally
;
GO
 CREATE FUNCTION [dbo].[fnTally]
/**********************************************************************************************************************
 Purpose:
 Return a column of BIGINTs from @ZeroOrOne up to and including @MaxN with a max value of 1 Trillion.

 As a performance note, it takes about 00:02:10 (hh:mm:ss) to generate 1 Billion numbers to a throw-away variable.

 Usage:
--===== Syntax example (Returns BIGINT)
 SELECT t.N
   FROM dbo.fnTally(@ZeroOrOne,@MaxN) t
;

 Notes:
 1. Based on Itzik Ben-Gan's cascading CTE (cCTE) method for creating a "readless" Tally Table source of BIGINTs.
    Refer to the following URLs for how it works and introduction for how it replaces certain loops. 
    http://www.sqlservercentral.com/articles/T-SQL/62867/
    http://sqlmag.com/sql-server/virtual-auxiliary-table-numbers
 2. To start a sequence at 0, @ZeroOrOne must be 0 or NULL. Any other value that's convertable to the BIT data-type
    will cause the sequence to start at 1.
 3. If @ZeroOrOne = 1 and @MaxN = 0, no rows will be returned.
 5. If @MaxN is negative or NULL, a "TOP" error will be returned.
 6. @MaxN must be a positive number from >= the value of @ZeroOrOne up to and including 1 Billion. If a larger
    number is used, the function will silently truncate after 1 Billion. If you actually need a sequence with
    that many values, you should consider using a different tool. ;-)
 7. There will be a substantial reduction in performance if "N" is sorted in descending order.  If a descending 
    sort is required, use code similar to the following. Performance will decrease by about 27% but it's still
    very fast especially compared with just doing a simple descending sort on "N", which is about 20 times slower.
    If @ZeroOrOne is a 0, in this case, remove the "+1" from the code.

    DECLARE @MaxN BIGINT; 
     SELECT @MaxN = 1000;
     SELECT DescendingN = @MaxN-N+1 
       FROM dbo.fnTally(1,@MaxN);

 8. There is no performance penalty for sorting "N" in ascending order because the output is explicity sorted by
    ROW_NUMBER() OVER (ORDER BY (SELECT NULL))

 Revision History:
 Rev 00 - Unknown     - Jeff Moden 
        - Initial creation with error handling for @MaxN.
 Rev 01 - 09 Feb 2013 - Jeff Moden 
        - Modified to start at 0 or 1.
 Rev 02 - 16 May 2013 - Jeff Moden 
        - Removed error handling for @MaxN because of exceptional cases.
 Rev 03 - 22 Apr 2015 - Jeff Moden
        - Modify to handle 1 Trillion rows for experimental purposes.
**********************************************************************************************************************/
        (@ZeroOrOne BIT, @MaxN BIGINT)
RETURNS TABLE WITH SCHEMABINDING AS 
 RETURN WITH
  E1(N) AS (SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
            SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
            SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
            SELECT 1)                                  --10E1 or 10 rows
, E4(N) AS (SELECT 1 FROM E1 a, E1 b, E1 c, E1 d)      --10E4 or 10 Thousand rows
,E12(N) AS (SELECT 1 FROM E4 a, E4 b, E4 c)            --10E12 or 1 Trillion rows                 
            SELECT N = 0 WHERE ISNULL(@ZeroOrOne,0)= 0 --Conditionally start at 0.
             UNION ALL 
            SELECT TOP(@MaxN) N = ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E12 -- Values from 1 to @MaxN
;
GO

โดยวิธีการ ... สังเกตเห็นว่าสร้างตาราง Tally Table หนึ่งล้านและหนึ่งแถวและเพิ่มดัชนีแบบกลุ่มในดัชนีประมาณหนึ่งวินาที ลองใช้ด้วย rCTE และดูว่าใช้เวลานานเท่าใด! ;-)

สร้างข้อมูลการทดสอบบางส่วน

เราต้องการข้อมูลการทดสอบด้วย ใช่ฉันยอมรับว่าฟังก์ชั่นทั้งหมดที่เรากำลังจะทำการทดสอบรวมถึง rCTE ทำงานเป็นมิลลิวินาทีหรือน้อยกว่าในเวลาเพียง 12 แถว แต่นั่นเป็นกับดักที่ผู้คนจำนวนมากตกอยู่ใน เราจะพูดเพิ่มเติมเกี่ยวกับกับดักในภายหลัง แต่สำหรับตอนนี้ลองจำลองการโทรแต่ละฟังก์ชั่น 40,000 ครั้งซึ่งประมาณว่าฟังก์ชั่นบางอย่างในร้านของฉันถูกเรียกในเวลา 8 ชั่วโมงต่อวัน แค่คิดว่าอาจมีการเรียกใช้ฟังก์ชั่นนี้กี่ครั้งในธุรกิจค้าปลีกออนไลน์ขนาดใหญ่

ดังนั้นนี่คือรหัสในการสร้าง 40,000 แถวด้วยวันที่สุ่มโดยแต่ละครั้งจะมีหมายเลขแถวเพื่อการติดตาม ฉันไม่ได้ใช้เวลาเพื่อให้เวลาทั้งชั่วโมงเพราะมันไม่สำคัญ

--===== Do this in a nice, safe place that everyone has
    USE tempdb
;
--===== Create/Recreate a Test Date table
     IF OBJECT_ID('dbo.TestDate','U') IS NOT NULL
        DROP TABLE dbo.TestDate
;
DECLARE  @StartDate DATETIME
        ,@EndDate   DATETIME
        ,@Rows      INT
;
 SELECT  @StartDate = '2010' --Inclusive
        ,@EndDate   = '2020' --Exclusive
        ,@Rows      = 40000  --Enough to simulate an 8 hour day where I work
;
 SELECT  RowNum       = IDENTITY(INT,1,1)
        ,SomeDateTime = RAND(CHECKSUM(NEWID()))*DATEDIFF(dd,@StartDate,@EndDate)+@StartDate
   INTO dbo.TestDate
   FROM dbo.fnTally(1,@Rows)
;

สร้างฟังก์ชั่นบางอย่างเพื่อทำสิ่ง 12 แถวต่อชั่วโมง

ถัดไปฉันแปลงโค้ด rCTE เป็นฟังก์ชันและสร้างอีก 3 ฟังก์ชัน พวกเขาทั้งหมดถูกสร้างขึ้นเป็น iTVF ที่มีประสิทธิภาพสูง (ฟังก์ชั่นที่มีค่าในตารางแบบอินไลน์) คุณสามารถบอกได้เสมอเพราะ iTVF ไม่มี BEGIN ในตัวเช่น Scalar หรือ mTVFs (ฟังก์ชั่นประเมินมูลค่าตารางแบบหลายคำสั่ง)

นี่คือรหัสในการสร้างฟังก์ชั่นทั้งสี่นั้น ... ฉันตั้งชื่อพวกเขาหลังจากวิธีที่พวกเขาใช้และไม่ใช่สิ่งที่พวกเขาทำเพียงเพื่อให้ง่ายต่อการระบุพวกเขา

--=====  CREATE THE iTVFs
--===== Do this in a nice, safe place that everyone has
    USE tempdb
;
-----------------------------------------------------------------------------------------
     IF OBJECT_ID('dbo.OriginalrCTE','IF') IS NOT NULL
        DROP FUNCTION dbo.OriginalrCTE
;
GO
 CREATE FUNCTION dbo.OriginalrCTE
        (@Date DATETIME)
RETURNS TABLE WITH SCHEMABINDING AS
 RETURN
WITH Dates AS
(
    SELECT DATEPART(HOUR,DATEADD(HOUR,-1,@Date)) [Hour], 
      DATEADD(HOUR,-1,@Date) [Date], 1 Num
    UNION ALL
    SELECT DATEPART(HOUR,DATEADD(HOUR,-1,[Date])), 
      DATEADD(HOUR,-1,[Date]), Num+1
    FROM Dates
    WHERE Num <= 11
)
SELECT [Hour], [Date]
FROM Dates
GO
-----------------------------------------------------------------------------------------
     IF OBJECT_ID('dbo.MicroTally','IF') IS NOT NULL
        DROP FUNCTION dbo.MicroTally
;
GO
 CREATE FUNCTION dbo.MicroTally
        (@Date DATETIME)
RETURNS TABLE WITH SCHEMABINDING AS
 RETURN
 SELECT  [Hour] = DATEPART(HOUR,DATEADD(HOUR,t.N,@Date))
        ,[DATE] = DATEADD(HOUR,t.N,@Date)
   FROM (VALUES (-1),(-2),(-3),(-4),(-5),(-6),(-7),(-8),(-9),(-10),(-11),(-12))t(N)
;
GO
-----------------------------------------------------------------------------------------
     IF OBJECT_ID('dbo.PhysicalTally','IF') IS NOT NULL
        DROP FUNCTION dbo.PhysicalTally
;
GO
 CREATE FUNCTION dbo.PhysicalTally
        (@Date DATETIME)
RETURNS TABLE WITH SCHEMABINDING AS
 RETURN
 SELECT  [Hour] = DATEPART(HOUR,DATEADD(HOUR,-t.N,@Date))
        ,[DATE] = DATEADD(HOUR,-t.N,@Date)
   FROM dbo.Tally t
  WHERE N BETWEEN 1 AND 12
;
GO
-----------------------------------------------------------------------------------------
     IF OBJECT_ID('dbo.TallyFunction','IF') IS NOT NULL
        DROP FUNCTION dbo.TallyFunction
;
GO
 CREATE FUNCTION dbo.TallyFunction
        (@Date DATETIME)
RETURNS TABLE WITH SCHEMABINDING AS
 RETURN
 SELECT  [Hour] = DATEPART(HOUR,DATEADD(HOUR,-t.N,@Date))
        ,[DATE] = DATEADD(HOUR,-t.N,@Date)
   FROM dbo.fnTally(1,12) t
;
GO

สร้างชุดทดสอบเพื่อทดสอบฟังก์ชั่น

สุดท้าย แต่ไม่ท้ายสุดเราต้องใช้สายรัดทดสอบ ฉันจะตรวจสอบพื้นฐานแล้วทดสอบแต่ละฟังก์ชั่นในลักษณะที่เหมือนกัน

นี่คือรหัสสำหรับชุดทดสอบ ...

PRINT '--========== Baseline Select =================================';
DECLARE @Hour INT, @Date DATETIME
;
    SET STATISTICS TIME,IO ON;
 SELECT  @Hour = RowNum
        ,@Date = SomeDateTime
   FROM dbo.TestDate
  CROSS APPLY dbo.fnTally(1,12);
    SET STATISTICS TIME,IO OFF;
GO
PRINT '--========== Orginal Recursive CTE ===========================';
DECLARE @Hour INT, @Date DATETIME
;

    SET STATISTICS TIME,IO ON;
 SELECT  @Hour = fn.[Hour]
        ,@Date = fn.[Date]
   FROM dbo.TestDate td
  CROSS APPLY dbo.OriginalrCTE(td.SomeDateTime) fn;
    SET STATISTICS TIME,IO OFF;
GO
PRINT '--========== Dedicated Micro-Tally Table =====================';
DECLARE @Hour INT, @Date DATETIME
;

    SET STATISTICS TIME,IO ON;
 SELECT  @Hour = fn.[Hour]
        ,@Date = fn.[Date]
   FROM dbo.TestDate td
  CROSS APPLY dbo.MicroTally(td.SomeDateTime) fn;
    SET STATISTICS TIME,IO OFF;
GO
PRINT'--========== Physical Tally Table =============================';
DECLARE @Hour INT, @Date DATETIME
;
    SET STATISTICS TIME,IO ON;
 SELECT  @Hour = fn.[Hour]
        ,@Date = fn.[Date]
   FROM dbo.TestDate td
  CROSS APPLY dbo.PhysicalTally(td.SomeDateTime) fn;
    SET STATISTICS TIME,IO OFF;
GO
PRINT'--========== Tally Function ===================================';
DECLARE @Hour INT, @Date DATETIME
;
    SET STATISTICS TIME,IO ON;
 SELECT  @Hour = fn.[Hour]
        ,@Date = fn.[Date]
   FROM dbo.TestDate td
  CROSS APPLY dbo.TallyFunction(td.SomeDateTime) fn;
    SET STATISTICS TIME,IO OFF;
GO

สิ่งหนึ่งที่สังเกตได้ในชุดทดสอบด้านบนคือฉันจะแบ่งเอาต์พุตทั้งหมดเป็นตัวแปร "throwaway" นั่นคือเพื่อพยายามรักษาประสิทธิภาพการวัดให้บริสุทธิ์ที่สุดเท่าที่จะเป็นไปได้โดยไม่มีผลลัพธ์ใด ๆ ลงบนดิสก์หรือหน้าจอที่บิดเบือน

คำเตือนเกี่ยวกับสถิติการตั้งค่า

นอกจากนี้ยังมีคำเตือนสำหรับผู้ที่จะทดสอบ ... คุณจะต้องไม่ใช้ SET Statistics เมื่อทำการทดสอบฟังก์ชั่น Scalar หรือ mTVF สามารถใช้งานได้อย่างปลอดภัยกับฟังก์ชั่น iTVF เช่นเดียวกับที่ใช้ในการทดสอบนี้ สถิติของตลาดหลักทรัพย์ได้รับการพิสูจน์แล้วว่าทำให้ฟังก์ชัน SCALAR ทำงานช้าลงกว่าที่พวกเขาทำไว้หลายร้อยเท่า ใช่ฉันพยายามเอียงกังหันลมอีกเครื่อง แต่นั่นจะเป็นบทความที่มีความยาวทั้งหมดและฉันไม่มีเวลาทำเช่นนั้น ฉันมีบทความเกี่ยวกับ SQLServerCentral.com ที่พูดถึงเรื่องนี้ทั้งหมด แต่ไม่มีเหตุผลในการโพสต์ลิงค์ที่นี่เพราะบางคนจะงอออกจากรูปร่างเกี่ยวกับมัน

ผลการทดสอบ

ดังนั้นนี่คือผลการทดสอบเมื่อฉันรันชุดทดสอบบนแล็ปท็อป i5 ตัวเล็ก ๆ ที่มี RAM ขนาด 6GB

--========== Baseline Select =================================
Table 'Worktable'. Scan count 1, logical reads 82309, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'TestDate'. Scan count 1, logical reads 105, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 203 ms,  elapsed time = 206 ms.
--========== Orginal Recursive CTE ===========================
Table 'Worktable'. Scan count 40001, logical reads 2960000, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'TestDate'. Scan count 1, logical reads 105, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 4258 ms,  elapsed time = 4415 ms.
--========== Dedicated Micro-Tally Table =====================
Table 'Worktable'. Scan count 1, logical reads 81989, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'TestDate'. Scan count 1, logical reads 105, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 234 ms,  elapsed time = 235 ms.
--========== Physical Tally Table =============================
Table 'Worktable'. Scan count 1, logical reads 81989, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'TestDate'. Scan count 1, logical reads 105, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Tally'. Scan count 1, logical reads 3, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 250 ms,  elapsed time = 252 ms.
--========== Tally Function ===================================
Table 'Worktable'. Scan count 1, logical reads 81989, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'TestDate'. Scan count 1, logical reads 105, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 250 ms,  elapsed time = 253 ms.

"เลือกขั้นพื้นฐาน" ซึ่งเลือกเฉพาะข้อมูล (แต่ละแถวสร้าง 12 ครั้งเพื่อจำลองปริมาณการส่งคืนเดียวกัน) มาทางขวาประมาณ 1 / 5th ของวินาที ทุกอย่างเข้ามาในเวลาประมาณหนึ่งในสี่ของวินาที ทุกอย่างยกเว้นฟังก์ชั่น rCTE เลือด ใช้เวลา 4 และ 1/4 วินาทีหรือนานกว่า 16 เท่า (ช้ากว่า 1,600%)

และดูที่การอ่านแบบลอจิคัล (หน่วยความจำ IO) ... rCTE ใช้งาน 2,960,000 (เกือบ 3 ล้านอ่าน) ในขณะที่ฟังก์ชั่นอื่น ๆ ใช้เพียงประมาณ 82,100 นั่นหมายความว่า rCTE ใช้หน่วยความจำ IO มากกว่า 34.3 เท่าของฟังก์ชั่นอื่น ๆ

ความคิดที่ปิด

มาสรุปกัน วิธี rCTE สำหรับทำสิ่ง 12 แถว "เล็ก" นี้ใช้ซีพียู (และระยะเวลา) 16 ครั้ง (1,600%) และ 34.3 ครั้ง (3,430%) หน่วยความจำ IO มากกว่าฟังก์ชั่นอื่น ๆ

เฮ้ ... ฉันรู้ว่าคุณกำลังคิดอะไรอยู่ "เรื่องใหญ่! มันเป็นแค่ฟังก์ชั่นเดียว"

ใช่เห็นด้วย แต่คุณมีฟังก์ชั่นอื่นอีกกี่ตัว? คุณมีสถานที่อื่นอีกกี่แห่งนอกฟังก์ชั่น? และคุณมีผู้ที่ทำงานมากกว่า 12 แถวในแต่ละครั้งหรือไม่? และมีโอกาสที่ใครบางคนในเซถลาวิธีอาจคัดลอกรหัส rCTE สำหรับสิ่งที่ใหญ่กว่ามาก?

ตกลงเวลาที่จะทื่อ มันไม่สมเหตุสมผลเลยที่คนจะพิสูจน์ประสิทธิภาพของรหัสที่ท้าทายเนื่องจากจำนวนแถวหรือการใช้งานที่ จำกัด ยกเว้นเมื่อคุณซื้อกล่อง MPP สำหรับหลายล้านดอลลาร์ (ไม่ต้องพูดถึงค่าใช้จ่ายของการเขียนรหัสใหม่เพื่อให้มันทำงานบนเครื่องดังกล่าวได้) คุณไม่สามารถซื้อเครื่องที่รันรหัสของคุณได้เร็วขึ้น 16 เท่า (ชนะของ SSD อย่าทำเช่นนี้ ... ทุกสิ่งนี้อยู่ในหน่วยความจำความเร็วสูงเมื่อเราทำการทดสอบ) ประสิทธิภาพการทำงานอยู่ในรหัส ประสิทธิภาพที่ดีอยู่ในรหัสที่ดี

คุณจินตนาการได้ไหมว่ารหัสทั้งหมดของคุณทำงานเร็วขึ้น 16 เท่า?

อย่าปรับรหัสที่ไม่ถูกต้องหรือประสิทธิภาพที่ท้าทายให้กับจำนวนแถวต่ำหรือแม้แต่การใช้งานต่ำ หากคุณทำเช่นนั้นคุณอาจต้องยืมหนึ่งในกังหันลมที่ฉันถูกกล่าวหาว่าเอียงที่จะทำให้ซีพียูและดิสก์ของคุณเย็นพอ ;-)

WORD ON WORD "TALLY"

ใช่ ... ฉันเห็นด้วย การพูดความหมายตาราง Tally มีตัวเลขไม่ใช่ "การนับ" ในบทความต้นฉบับของฉันเกี่ยวกับหัวเรื่อง (ไม่ใช่บทความต้นฉบับเกี่ยวกับเทคนิค แต่เป็นบทความแรกของฉัน) ฉันเรียกมันว่า "Tally" ไม่ใช่เพราะสิ่งที่มีอยู่ แต่เพราะสิ่งที่มันทำ ... มันเป็น ใช้เพื่อ "นับ" แทนการวนซ้ำและบางสิ่งที่ "นับ" คือการนับบางสิ่ง ;-) เรียกมันว่าสิ่งที่คุณจะ ... ตารางตัวเลขโต๊ะ Tally, ตารางลำดับอะไรก็ตาม ฉันไม่สนใจ สำหรับฉัน "Tally" นั้นมีความหมายมากกว่าและเป็น DBA ขี้เกียจที่ดีมีเพียง 5 ตัวอักษร (2 เหมือนกัน) แทนที่จะเป็น 7 และง่ายกว่าที่จะพูดกับคนส่วนใหญ่ นอกจากนี้ยังเป็น "เอกพจน์" ซึ่งทำตามแบบแผนการตั้งชื่อของฉันสำหรับตาราง ;-) มัน ' s สิ่งที่บทความที่มีหน้าจากหนังสือจาก 60 ที่เรียกว่า ฉันมักจะอ้างถึงมันเป็น "Tally Table" และคุณจะยังคงรู้ว่าฉันหรือคนอื่นหมายถึงอะไร ฉันยังหลีกเลี่ยงสัญลักษณ์ฮังการีเหมือนกาฬโรค แต่เรียกใช้ฟังก์ชัน "fnTally" เพื่อที่ฉันจะได้พูดว่า "ดีถ้าคุณใช้ฟังก์ชั่น Tally-en Tally ที่ฉันแสดงให้คุณเห็นคุณจะไม่มีปัญหาเรื่องประสิทธิภาพ" โดยแท้จริงแล้ว การละเมิดทรัพยากรบุคคล ;-) จริงๆแล้วมันจะไม่เป็นการละเมิดทรัพยากรบุคคล ;-) จริงๆแล้วมันจะไม่เป็นการละเมิดทรัพยากรบุคคล ;-)

สิ่งที่ฉันกังวลมากกว่าคือคนที่เรียนรู้ที่จะใช้อย่างถูกต้องแทนที่จะหันไปใช้สิ่งต่าง ๆ เช่นประสิทธิภาพที่ท้าทาย rCTEs และ Hidden RBAR ในรูปแบบอื่น ๆ


2

คุณจะต้องใช้RIGHT JOINข้อมูลของคุณพร้อมกับแบบสอบถามที่ส่งคืนระเบียนหนึ่งระเบียนทุกชั่วโมงที่คุณต้องการ

ดูวิธีนี้สองสามวิธีในการรับหมายเลขแถวซึ่งคุณสามารถลบออกเป็นชั่วโมงจากเวลาปัจจุบันได้

ใน Oracle แบบสอบถามแบบลำดับชั้นของทั้งคู่จะสร้างแถว:

SELECT to_char(sysdate-level/24,'HH24') FROM dual CONNECT BY Level <=24;

เป็น "แบบสอบถามส่งคืนหนึ่งระเบียนทุกชั่วโมง" ที่ฉันมีปัญหา แค่พยายามคิดหาวิธีสร้างเรกคอร์ด 12 (หรือ 24) ด้วยทุก ๆ ชั่วโมงของ 12 (หรือ 24) ชั่วโมงที่ผ่านมา
datagod
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.