SQL حدد بيان لحساب عمود المتوسط الجاري
-
06-09-2019 - |
سؤال
أحاول الحصول على عمود متوسط قيد التشغيل في عبارة 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 إلى الجدول، بما في ذلك القدرة على كتابة وظائف تجميعية للمستخدم.سيكون إجمالي إجمالي التشغيل المخصص هو الطريقة الأكثر فعالية لحساب متوسط تشغيل مثل هذا، إلى حد بعيد.
وبدلاً من ذلك، يمكنك إلغاء تسوية قيم التشغيل المحسوبة مسبقًا وتخزينها.الموصوفة هنا:
أداء التحديدات سريع كما هو الحال.وبطبيعة الحال، التعديلات أبطأ.