سؤال

أحاول الحصول على عمود متوسط ​​قيد التشغيل في عبارة SELECT بناءً على عمود من الصفوف السابقة في نفس عبارة SELECT.يعتمد المتوسط ​​الذي أحتاجه على الصفوف السابقة في مجموعة النتائج.

دعني أشرح

Id        Number       Average
 1             1          NULL
 2             3          NULL
 3             2          NULL
 4             4             2 <----- Average of (1, 3, 2),Numbers from previous 3 rows
 5             6             3 <----- Average of (3, 2, 4),Numbers from previous 3 rows
 .             .             .
 .             .             .

الصفوف الثلاثة الأولى من عمود "المتوسط" فارغة نظرًا لعدم وجود صفوف سابقة.يعرض الصف 4 في عمود "المتوسط" متوسط ​​عمود "الرقم" من الصفوف الثلاثة السابقة.

أحتاج إلى بعض المساعدة في محاولة إنشاء عبارة SQL Select التي ستقوم بذلك.

هل كانت مفيدة؟

المحلول

هذا يجب أن يفعل ذلك:

--Test Data
CREATE TABLE    RowsToAverage
    (
    ID int NOT NULL,
    Number int NOT NULL
    )

INSERT  RowsToAverage(ID, Number)
SELECT  1, 1
UNION ALL
SELECT  2, 3
UNION ALL
SELECT  3, 2
UNION ALL
SELECT  4, 4
UNION ALL
SELECT  5, 6
UNION ALL
SELECT  6, 8
UNION ALL
SELECT  7, 10

--The query
;WITH   NumberedRows
AS
(
SELECT  rta.*, row_number() OVER (ORDER BY rta.ID ASC) AS RowNumber
FROM    RowsToAverage rta
)

SELECT  nr.ID, nr.Number,
        CASE
            WHEN nr.RowNumber <=3 THEN NULL
            ELSE (  SELECT  avg(Number) 
                    FROM    NumberedRows 
                    WHERE   RowNumber < nr.RowNumber
                    AND     RowNumber >= nr.RowNumber - 3
                )
        END AS MovingAverage
FROM    NumberedRows nr

نصائح أخرى

بافتراض أن عمود المعرف متسلسل، إليك استعلام مبسط لجدول يسمى "MyTable":

SELECT 
    b.Id,
    b.Number,
    (
      SELECT 
       AVG(a.Number) 
      FROM 
       MyTable a 
     WHERE 
       a.id >= (b.Id - 3) 
       AND a.id < b.Id
       AND b.Id > 3 
     ) as Average
FROM 
    MyTable b;

يبدو أن الانضمام الذاتي البسيط يؤدي أداءً أفضل بكثير من الاستعلام الفرعي المرجعي للصف

إنشاء 10 آلاف صف من بيانات الاختبار:

drop table test10k
create table test10k (Id int, Number int, constraint test10k_cpk primary key clustered (id))

;WITH digits AS (
    SELECT 0 as Number
    UNION SELECT 1
    UNION SELECT 2
    UNION SELECT 3
    UNION SELECT 4
    UNION SELECT 5
    UNION SELECT 6
    UNION SELECT 7
    UNION SELECT 8
    UNION SELECT 9
)
,numbers as (
    SELECT 
        (thousands.Number * 1000) 
        + (hundreds.Number * 100) 
        + (tens.Number * 10) 
        + ones.Number AS Number
    FROM digits AS ones 
    CROSS JOIN digits AS tens
    CROSS JOIN digits AS hundreds
    CROSS JOIN digits AS thousands
)
insert test10k (Id, Number)
select Number, Number
from numbers 

أود أن أسحب الحالة الخاصة للصفوف الثلاثة الأولى من الاستعلام الرئيسي، ويمكنك توحيد كل تلك الصفوف مرة أخرى إذا كنت تريد ذلك حقًا في مجموعة الصفوف.استعلام الانضمام الذاتي:

;WITH   NumberedRows
AS
(
    SELECT  rta.*, row_number() OVER (ORDER BY rta.ID ASC) AS RowNumber
    FROM    test10k rta
)

SELECT  nr.ID, nr.Number,
    avg(trailing.Number) as MovingAverage
FROM    NumberedRows nr
    join NumberedRows as trailing on trailing.RowNumber between nr.RowNumber-3 and nr.RowNumber-1
where nr.Number > 3
group by nr.id, nr.Number

يستغرق هذا على جهازي حوالي 10 ثوانٍ، ويستغرق أسلوب الاستعلام الفرعي الذي أظهره آرون ألتون حوالي 45 ثانية (بعد أن قمت بتغييره ليعكس جدول مصدر الاختبار الخاص بي):

;WITH   NumberedRows
AS
(
    SELECT  rta.*, row_number() OVER (ORDER BY rta.ID ASC) AS RowNumber
    FROM    test10k rta
)
SELECT  nr.ID, nr.Number,
    CASE
            WHEN nr.RowNumber <=3 THEN NULL
            ELSE (  SELECT  avg(Number) 
                            FROM    NumberedRows 
                            WHERE   RowNumber < nr.RowNumber
                            AND             RowNumber >= nr.RowNumber - 3
                    )
    END AS MovingAverage
FROM    NumberedRows nr

إذا قمت بتشغيل SET STATISTICS PROFILE، فيمكنك رؤية أن الانضمام الذاتي يتم تنفيذه بمقدار 10 كيلو بايت على التخزين المؤقت للجدول.يحتوي الاستعلام الفرعي على 10 آلاف عملية تنفيذ على عوامل التصفية والتجميع والخطوات الأخرى.

يحرر:فاتني نقطة أنه ينبغي أن يكون متوسط ​​السجلات الثلاثة السابقة ...

بالنسبة لمتوسط ​​التشغيل العام، أعتقد أن شيئًا كهذا قد ينجح:

SELECT
    id, number, 
    SUM(number) OVER (ORDER BY ID) / 
       ROW_NUMBER() OVER (ORDER BY ID) AS [RunningAverage]
FROM myTable
ORDER BY ID

تحقق من بعض الحلول هنا.أنا متأكد من أنه يمكنك التكيف مع واحد منهم بسهولة كافية.

إذا كنت تريد أن يكون هذا الأداء فعالاً حقًا، ولا تخشى البحث في منطقة نادرًا ما تستخدم في SQL Server، فيجب عليك التفكير في كتابة دالة مجمعة مخصصة.جلب SQL Server 2005 و2008 تكامل CLR إلى الجدول، بما في ذلك القدرة على كتابة وظائف تجميعية للمستخدم.سيكون إجمالي إجمالي التشغيل المخصص هو الطريقة الأكثر فعالية لحساب متوسط ​​تشغيل مثل هذا، إلى حد بعيد.

وبدلاً من ذلك، يمكنك إلغاء تسوية قيم التشغيل المحسوبة مسبقًا وتخزينها.الموصوفة هنا:

http://sqlblog.com/blogs/alexander_kuznetsov/archive/2009/01/23/denormalizing-to-enforce-business-rules-running-totals.aspx

أداء التحديدات سريع كما هو الحال.وبطبيعة الحال، التعديلات أبطأ.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top