تحتاج إلى عدد الصفوف بعد عبارة SELECT:ما هو نهج SQL الأمثل؟

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

  •  04-07-2019
  •  | 
  •  

سؤال

أحاول تحديد عمود من جدول واحد (بدون صلات) وأحتاج إلى حساب عدد الصفوف، بشكل مثالي قبل أن أبدأ في استرداد الصفوف.لقد توصلت إلى نهجين يوفران المعلومات التي أحتاجها.

النهج 1:

SELECT COUNT( my_table.my_col ) AS row_count
  FROM my_table
 WHERE my_table.foo = 'bar'

ثم

SELECT my_table.my_col
  FROM my_table
 WHERE my_table.foo = 'bar'

أو النهج 2

SELECT my_table.my_col, ( SELECT COUNT ( my_table.my_col )
                            FROM my_table
                           WHERE my_table.foo = 'bar' ) AS row_count
  FROM my_table
 WHERE my_table.foo = 'bar'

أفعل هذا لأن برنامج تشغيل SQL الخاص بي (SQL Native Client 9.0) لا يسمح لي باستخدام SQLRowCount في عبارة SELECT ولكني بحاجة إلى معرفة عدد الصفوف في نتيجتي لتخصيص صفيف قبل تعيين المعلومات إليه.لسوء الحظ، فإن استخدام الحاوية المخصصة ديناميكيًا ليس خيارًا في هذا المجال من برنامجي.

أشعر بالقلق من احتمال حدوث السيناريو التالي:

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

هل يحظر النهج 2 هذه المشكلة؟

وأيضاً هل سيكون أحد الطريقتين أسرع؟إذا كان الأمر كذلك، أي؟

أخيرًا، هل هناك طريقة أفضل يجب أن أفكر فيها (ربما طريقة لإرشاد برنامج التشغيل لإرجاع عدد الصفوف في نتيجة SELECT باستخدام SQLRowCount؟)

بالنسبة لأولئك الذين سألوا، أنا أستخدم Native C++ مع برنامج تشغيل SQL المذكور أعلاه (المقدم من Microsoft.)

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

المحلول

هناك طريقتان فقط للتأكد بنسبة 100% من أن COUNT(*) وسيعطي الاستعلام الفعلي نتائج متسقة:

  • الجمع بين COUNT(*) مع الاستعلام، كما هو الحال في النهج 2.أوصي بالنموذج الذي تظهره في المثال الخاص بك، وليس نموذج الاستعلام الفرعي المرتبط الموضح في تعليق kogus.
  • استخدم استعلامين، كما في النهج 1، بعد بدء المعاملة SNAPSHOT أو SERIALIZABLE مستوى العزلة.

يعد استخدام أحد مستويات العزل هذه أمرًا مهمًا لأن أي مستوى عزل آخر يسمح للصفوف الجديدة التي أنشأها العملاء الآخرون بأن تصبح مرئية في معاملتك الحالية.اقرأ وثائق MSDN على SET TRANSACTION ISOLATION لمزيد من التفاصيل.

نصائح أخرى

إذا كنت تستخدم SQL Server، بعد الاستعلام، يمكنك تحديد @@ عدد الصفوف الدالة (أو إذا كانت مجموعة النتائج الخاصة بك تحتوي على أكثر من 2 مليار صف، فاستخدم الدالة RowCount_Big() وظيفة).سيؤدي هذا إلى إرجاع عدد الصفوف المحددة بواسطة العبارة السابقة أو عدد الصفوف المتأثرة ببيان إدراج/تحديث/حذف.

SELECT my_table.my_col
  FROM my_table
 WHERE my_table.foo = 'bar'

SELECT @@Rowcount

أو إذا كنت تريد تضمين عدد الصفوف في النتيجة المرسلة بشكل مشابه للنهج رقم 2، فيمكنك استخدام الملف جملة "OVER"..

SELECT my_table.my_col,
    count(*) OVER(PARTITION BY my_table.foo) AS 'Count'
  FROM my_table
 WHERE my_table.foo = 'bar'

سيكون لاستخدام جملة OVER أداء أفضل بكثير من استخدام استعلام فرعي للحصول على عدد الصفوف.سيؤدي استخدام @@RowCount إلى الحصول على أفضل أداء لأنه لن تكون هناك أي تكلفة استعلام لعبارة @@RowCount المحددة

تحديث ردا على التعليق:المثال الذي قدمته سيعطي عدد الصفوف في القسم - المحدد في هذه الحالة بواسطة "PARTITION BY my_table.foo".قيمة العمود في كل صف هي عدد الصفوف التي لها نفس قيمة my_table.foo.نظرًا لأن استعلام المثال الخاص بك يحتوي على العبارة "WHERE my_table.foo = 'bar'"، فإن جميع الصفوف في مجموعة النتائج سيكون لها نفس قيمة my_table.foo وبالتالي فإن القيمة الموجودة في العمود ستكون هي نفسها لجميع الصفوف ومتساوية (في هذه الحالة) هذا هو عدد الصفوف في الاستعلام.

فيما يلي مثال أفضل/أبسط لكيفية تضمين عمود في كل صف يمثل إجمالي عدد الصفوف في مجموعة النتائج.ما عليك سوى إزالة جملة Partition By الاختيارية.

SELECT my_table.my_col, count(*) OVER() AS 'Count'
  FROM my_table
 WHERE my_table.foo = 'bar'

سيُرجع النهج 2 دائمًا عددًا يطابق مجموعة النتائج الخاصة بك.

أقترح عليك ربط الاستعلام الفرعي باستعلامك الخارجي، لضمان تطابق الشرط الموجود في العدد الخاص بك مع الشرط الموجود في مجموعة البيانات.

SELECT 
  mt.my_row,
 (SELECT COUNT(mt2.my_row) FROM my_table mt2 WHERE mt2.foo = mt.foo) as cnt
FROM my_table mt
WHERE mt.foo = 'bar';

إذا كنت قلقًا من أن عدد الصفوف التي تستوفي الشرط قد يتغير خلال بضعة أجزاء من الثانية منذ تنفيذ الاستعلام واسترداد النتائج، فيمكنك/ينبغي عليك تنفيذ الاستعلامات داخل المعاملة:

BEGIN TRAN bogus

SELECT COUNT( my_table.my_col ) AS row_count
FROM my_table
WHERE my_table.foo = 'bar'

SELECT my_table.my_col
FROM my_table
WHERE my_table.foo = 'bar'
ROLLBACK TRAN bogus

سيؤدي هذا إلى إرجاع القيم الصحيحة دائمًا.

علاوة على ذلك، إذا كنت تستخدم SQL Server، فيمكنك استخدام @@ROWCOUNT للحصول على عدد الصفوف المتأثرة بالعبارة الأخيرة، وإعادة توجيه إخراج حقيقي الاستعلام إلى جدول مؤقت أو متغير جدول، حتى تتمكن من إرجاع كل شيء تمامًا، دون الحاجة إلى معاملة:

DECLARE @dummy INT

SELECT my_table.my_col
INTO #temp_table
FROM my_table
WHERE my_table.foo = 'bar'

SET @dummy=@@ROWCOUNT
SELECT @dummy, * FROM #temp_table

وهنا بعض الأفكار:

  • اتبع النهج رقم 1 وقم بتغيير حجم المصفوفة للاحتفاظ بنتائج إضافية أو استخدم نوعًا يتم تغيير حجمه تلقائيًا حسب الضرورة (لا تذكر اللغة التي تستخدمها لذا لا يمكنني أن أكون أكثر تحديدًا).
  • يمكنك تنفيذ كلا العبارتين في النهج رقم 1 ضمن معاملة لضمان أن تكون الأعداد متماثلة في المرتين إذا كانت قاعدة البيانات الخاصة بك تدعم ذلك.
  • لست متأكدًا مما تفعله بالبيانات، ولكن إذا كان من الممكن معالجة النتائج دون تخزينها جميعًا أولاً، فقد تكون هذه هي الطريقة الأفضل.

إذا كنت قلقًا حقًا من أن عدد الصفوف الخاص بك سيتغير بين عدد التحديد وعبارة التحديد، فلماذا لا تحدد صفوفك في جدول مؤقت أولاً؟بهذه الطريقة، ستعرف أنك ستكون متزامنًا.

لماذا لا تضع نتائجك في ناقل؟بهذه الطريقة لن تضطر إلى معرفة الحجم مسبقًا.

قد ترغب في التفكير في نمط أفضل للتعامل مع البيانات من هذا النوع.

لن يخبرك أي برنامج تشغيل SQL ذاتي التوقع بعدد الصفوف التي سيرجعها استعلامك قبل إرجاع الصفوف، لأن الإجابة قد تتغير (ما لم تستخدم معاملة، مما يخلق مشاكل خاصة به.)

لن يتغير عدد الصفوف - جوجل لـ ACID وSQL.

IF (@@ROWCOUNT > 0)
BEGIN
SELECT my_table.my_col
  FROM my_table
 WHERE my_table.foo = 'bar'
END

فقط لإضافة هذا لأن هذه هي النتيجة الأعلى في جوجل لهذا السؤال.في sqlite استخدمت هذا للحصول على عدد الصفوف.

WITH temptable AS
  (SELECT one,two
   FROM
     (SELECT one, two
      FROM table3
      WHERE dimension=0
      UNION ALL SELECT one, two
      FROM table2
      WHERE dimension=0
      UNION ALL SELECT one, two
      FROM table1
      WHERE dimension=0)
   ORDER BY date DESC)
SELECT *
FROM temptable
LEFT JOIN
  (SELECT count(*)/7 AS cnt,
                        0 AS bonus
   FROM temptable) counter
WHERE 0 = counter.bonus
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top