أفضل طريقة في MySQL أو Rails للحصول على AVG يوميًا ضمن نطاق تاريخ محدد

StackOverflow https://stackoverflow.com/questions/423212

سؤال

أحاول صنع رسم بياني في القضبان ، على سبيل المثال مبلغ مبيعات AVG يوميًا لكل يوم في نطاق تاريخ معين

لنفترض أن لدي نموذج Products_sold الذي يحتوي على سمة "sales_price". ولكن إذا كان يوم معين لا يحتوي على مبيعات (على سبيل المثال لا شيء في النموذج/DB) ، فأنا أريد العودة ببساطة 0.

ما هي أفضل طريقة في MySQL/Rails لإنجاز هذا؟ أعلم أنه يمكنني فعل شيء مثل هذا:

(قد يكون استعلام SQL هذا هو الطريقة الخاطئة تمامًا للحصول على ما أريده أيضًا)

SELECT avg(sales_price) AS avg, DATE_FORMAT(created_at, '%m-%d-%Y') AS date
    FROM products_sold WHERE merchant_id = 1 GROUP BY date;

والحصول على نتائج مثل هذا:

| avg |    date    |
  23    01-03-2009
  50    01-05-2009 
  34    01-07-2009
  ...       ...

ما أود الحصول عليه هو:

| avg |    date    |
  23    01-03-2009
   0    01-04-2009
  50    01-05-2009
   0    01-06-2009 
  34    01-07-2009
   0    01-08-2009
  ...       ...

هل يمكنني القيام بذلك باستخدام SQL أم سيتعين علي إجراء النتائج بعد المعالجة لتجد ما هي التواريخ في Daterange التي ليست في مجموعة نتائج SQL؟ ربما أحتاج إلى بعض عمليات الاختيار الفرعي أو إذا كانت البيانات؟

شكرا على أي مساعدة الجميع.

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

المحلول

هل هناك سبب (بخلاف التاريخ الذي سبق ذكره) لماذا لن تستخدم إمكانيات وظيفة المجموعة المضمنة في ActivereCord؟ يبدو أنك قلق بشأن "ما بعد المعالجة" ، وهو ما لا أعتقد أنه شيء يدعو للقلق حقًا.

أنت في القضبان ، لذلك ربما يجب أن تبحث عن حل القضبان أولاً [1]. فكرتي الأولى هي أن تفعل شيئًا مثل

Product.average(:sales_price, :group => "DATE(created_at)", :conditions => ["merchant_id=?", 1])

أي ActivereCord تحول إلى إلى حد كبير SQL الذي وصفته. على افتراض أن هناك إعلان has_many الارتباط بين التاجر والمنتج ، فمن المحتمل أن تكون أفضل استخدام ذلك ، لذلك شيء مثل:

ave_prices = Merchant.find(1).products.average(:sales_price, :group => "DATE(created_at)")

(آمل أن يكون وصفك للنموذج كـ "products_sold" نوعًا من خطأ النسخ ، راجع للشغل - إذا لم يكن الأمر كذلك ، فأنت خارج الحدود إلى حد ما مع تسمية الفصل!)

بعد كل ذلك ، ستعود إلى المكان الذي بدأت فيه ، لكنك وصلت إلى هناك بطريقة أكثر تقليدية (والقضبان حقًا تقدر الاتفاقيات!). الآن نحن بحاجة إلى ملء الفجوات.

سأفترض أنك تعرف نطاق تاريخك ، دعنا نقول أنه يتم تعريفه على أنه جميع التواريخ من from_date إلى to_date.

date_aves = (from_date..to_date).map{|dt| [dt, 0]}

التي تبني قائمة كاملة من التواريخ كصفيف. لا نحتاج إلى التواريخ التي حصلنا فيها على المتوسط:

ave_price_dates = ave_prices.collect{|ave_price| ave_price[0]} # build an array of dates
date_aves.delete_if { |dt| ave_price.dates.index(dt[0]) } # remove zero entries for dates retrieved from DB
date_aves.concat(ave_prices)     # add the query results
date_aves.sort_by{|ave| ave[0] } # sort by date

يبدو أن هذا الكثير متشوش قليلاً بالنسبة لي: أعتقد أنه يمكن أن يكون Terer و Plainer. سأقوم بالتحقيق في بناء تجزئة أو بنية بدلاً من البقاء في المصفوفات.


1] أنا لا أقول لا تستخدم SQL - تحدث المواقف حيث لا يمكن ActivereCord توليد الاستعلام الأكثر كفاءة وتتراجع find_by_sql. هذا جيد ، من المفترض أن يكون هكذا ، لكنني أعتقد أنه يجب عليك محاولة استخدامه فقط كملاذ أخير.

نصائح أخرى

لأي استفسار من هذا القبيل ، ستحتاج إلى العثور على آلية لإنشاء جدول مع صف واحد لكل تاريخ تريد الإبلاغ عنه. بعد ذلك ، ستقوم بربط خارجي لهذا الجدول مع جدول البيانات الذي تقوم بتحليله. قد تضطر أيضًا للعب مع NVL أو COALESCE لتحويل الفريدة إلى أصفار.

الجزء الصعب هو العمل على كيفية إنشاء الجدول (المؤقت) الذي يحتوي على قائمة التواريخ للنطاق الذي تحتاج إلى تحليله. هذا هو DBMS محدد.

إن فكرتك عن تعيين قيم التاريخ/الوقت إلى تاريخ واحد تكون على الرغم من ذلك. ستحتاج إلى سحب خدعة مماثلة - تعيين جميع التواريخ إلى تنسيق تاريخ ISO 8601 مثل 2009 -W01 للأسبوع 01 - إذا كنت ترغب في تحليل المبيعات الأسبوعية.

أيضًا ، من الأفضل أن تقوم بتخطيط تنسيق التاريخ الخاص بك إلى تدوين 2009-01-08 لأنه بعد ذلك يمكنك فرز ترتيب التاريخ باستخدام نوع حرف عادي.

لتجف قليلا:

ave_prices = Merchant.find(1).products.average(:sales_price, :group => "DATE(created_at)")
date_aves = (from_date..to_date).map{|dt| [dt, ave_prices[dt.strftime "%Y-%m-%d"] || 0]}

هل لدى MySQL وظائف إعادة تعيين؟ IE وظائف التي تُرجع قيمًا مختلفة في كل صف من الاستعلام؟ كمثال من PostgreSQL ، يمكنك القيام بذلك:

select 'foo', generate_series(3, 5);

سيؤدي ذلك إلى إنتاج مجموعة نتائج تتكون من عمودين و 3 صفوف ، حيث يحتوي العمود الأيسر على "foo" في كل صف ويحتوي العمود الأيمن على 3 و 4 و 5.

لذا ، على افتراض أن لديك ما يعادل generate_series() في mysql ، والحلول الفرعية: ما تحتاجه هو أ LEFT OUTER JOIN من هذه الوظيفة إلى الاستعلام الذي لديك بالفعل. سيضمن لك ذلك رؤية كل تاريخ يظهر في الإخراج:

SELECT
    avg(sales_price) as avg,
    DATE_FORMAT(the_date, '%m-%d-%Y') as date
FROM (select cast('2008-JAN-01' as date) + generate_series(0, 364) as the_date) date_range
LEFT OUTER JOIN products_sold on (the_date = created_at)
WHERE merchant_id = 1
GROUP BY date;

قد تحتاج إلى العبث مع هذا قليلاً للحصول على بناء الجملة من أجل MySQL.

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