سؤال

أنا أعمل حاليا على حالة استخدام معقدة بشكل خاص. تبسيط أدناه :)

أولا، يحتوي سجل العميل على علاقة متعددة إلى واحدة مع مجموعة من الخدمات، أي أن عميل واحد قد يكون لديه خدمات متعددة مرتبطة به.

ضمن الزناد الخاص بي، أكتب استفسارا بإرجاع معرف العميل بناء على معايير معينة. المعايير هي كما يلي،

  1. إذا كانت خدمة واحدة على الأقل من النوع B، ولا توجد خدمات من النوع موجود، ومعرف الإرجاع
  2. إذا كانت خدمة واحدة على الأقل من النوع C، ولا توجد خدمات من النوع B أو موجودة، ومعرف الإرجاع
  3. إذا كانت خدمة واحدة على الأقل من النوع D، ولا توجد خدمات من النوع C أو B أو موجودة، ومعرف الإرجاع

والنهج الحالي الخاص بي هو تشكيل استعلام مماثل لأحد أدناه

SELECT c.ClientId
FROM
  Clients AS c
    -- actually INNER JOIN is superfluous in this sample, but required for
    -- other auxilliary criteria i have left out. illustrates relationship
    -- between Clients and Services table
    INNER JOIN Services AS s ON c.ClientId = s.ClientId
WHERE
-- has at least one service of type B, no A
(EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'B')) AND
  NOT EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'A'))) OR 

-- has at least one service of type C, no B, no A
(EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'C')) AND
  NOT EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'B')) AND
  NOT EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'A'))) OR

-- has at least one service of type D, no C, no B, no A
(EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'D')) AND
  NOT EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'C')) AND
  NOT EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'B')) AND
  NOT EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'A')))

أين [dbo].[Get_ServicesByClientIdAndType] هي وظيفة تقوم بإرجاع الخدمات المرتبطة لمعرف العميل المحدد ونوع الخدمة. مشابه ل

-- this query is actually significantly more complex than shown
-- below, but this illustrates use of parameters client id and
-- service type
SELECT s.ServiceType
FROM
  Services AS s
WHERE
  s.ClientId = @clientId AND
  s.ServiceType = @serviceType

على افتراض أن هذه الوسيلة المثلى للتعبير عن هذه الحالة، ستعمل [dbo].[Get_ServicesByClientIdAndType] يتم تخزين الاستعلام الفرعي مخزنا مؤقتا أو هل تتطلب تغيير المعلمة الخدمة تقييم جديد لكل استدعاء؟ [أنا استدعاء هذا الشيء مثل 9 مرات !!! تشغيل SQL Server 2005

أعلم أن SQL Server 2005 يدعم بعض تحسينات الاستعلام الفرعية، مثل نتائج التخزين المؤقتة، لكنني لا أعرف بالتأكيد في ظل ظروف أو كيفية تشكيل استعلاماتي الفرعية [أو وظيفة] بحيث أقوم بأكثر من قدرات SQL Server.


تعديل: استعرض معاييري أعلاه، ولا يمكن أن تتخلى عن شعور مزعج لشيء ما كان خارج. لقد لعبت مع بعض المنطق في رأسي، وتوصلت إلى هذا [أكثر بساطة] صياغة

SELECT c.ClientId
FROM
  Clients AS c
    INNER JOIN Services AS s ON c.ClientId = s.ClientId
WHERE
  NOT EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'A')) AND
    (EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'B')) OR 
    EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'C')) OR 
    EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'D')))

في الأساس، لا يوجد أي سيناريو ينطوي على B الذي سيؤدي إلى الرفض، وبالمثل إلى C و D، لذلك أي تكوين مقبول. نحن نهتم فقط بأن A غير موجود في أي اختيار. arg! تشارلي براون!


ترك كلا التعبيرات لأعلى مراجعة، وما زلت أقدر الكثير من الاستجابات المتعلقة بأداء SQL Server WRT الوظائف المعرفة من المستخدمين.

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

المحلول

كنت أكتب إجابة لسؤالك وفي هذه الأثناء قمت بتغيير متطلباتك، لكن يجب ألا تواجه أي مشاكل لتحويل حولي لاحتياجاتك الخاصة ..

ولكن اسمحوا لي أن أبدأ من البداية. أنا متأكد من ذلك SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'A') لا يتم تخزين مؤقتا في أي حال بواسطة الخادم. ليس ذلك ذكيا؛) لذلك يتم حساب عدة مرات في الاستعلام الرئيسي الخاص بك.

لذلك يجب أن يذهب التحسين الأول في هذا الاتجاه. يجب أن تقلل من عدد المرات التي Get_ServicesByClientIdAndType يسمى. يمكنك أن تفعل ذلك بعدة طرق. لكن القاعدة العامة هي أنه يجب عليك حساب جميع النتائج الممكنة لهذه الوظيفة لجميع عملائك. يجب وضع هذه النتائج في طاولة مؤقتة أو سيتم وضعها في طاولة افتراضية يرصدها SQL Server نفسها.

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

بالطبع العديد من الأشياء والخدعة الأمثل تعتمد على مثالك الحقيقي. في المثال الذي قدمته، لا توجد حاجة للاستخدام Get_ServicesByClientIdAndTypeوبعد لماذا لا تنضم ببساطة إلى هذين الجداول وأداء بعض العمليات الحسابية عليها؟

إلقاء نظرة على هذا الاستعلام:

SELECT A.* FROM
(
 SELECT C.ClientID,
  SUM(CASE(S.ServiceType) WHEN 'A' THEN 1 ELSE 0 END) AS ServiceA,
  SUM(CASE(S.ServiceType) WHEN 'B' THEN 1 ELSE 0 END) AS ServiceB,
  SUM(CASE(S.ServiceType) WHEN 'C' THEN 1 ELSE 0 END) AS ServiceC,
  SUM(CASE(S.ServiceType) WHEN 'D' THEN 1 ELSE 0 END) AS ServiceD
 FROM Clients AS C
 INNER JOIN Services AS s ON c.ClientId = s.ClientId
 GROUP BY C.ClientID
) A
WHERE ((A.ServiceB > 0) AND (A.ServiceA = 0)) 
 OR ((A.ServiceC > 0) AND (A.ServiceA = 0) AND (A.ServiceB = 0))
 OR ((A.ServiceD > 0) AND (A.ServiceA = 0) AND (A.ServiceB = 0) AND (A.ServiceC = 0))

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

والنتيجة هي مثل هذا:

ClientID ServiceA ServiceB ServiceC ServiceD
-------- -------- -------- -------- --------
26915       0        4        2        2
26917       0        0        1        1
26921       0        3        2        3
26927       0        4        2        4

بالطبع يمكنك تجريد النتيجة النهائية من أعمدة الخدمة. لقد قمت بتضمينها لأنني أحب ذلك بهذه الطريقة ؛-) ويسمح للتحقق مما إذا كان الاستعلام يعمل بشكل صحيح. يمكنك حتى كتابة استعلام لن يحسب عدد نوع الخدمة المعطاة لعميل معين. سوف يعمل بشكل أسرع وتعطيك النتائج المناسبة.

أيضا إذا كنت بحاجة فعلا إلى وظيفتك، فلماذا لا تغير تنفيذها بطريقة تعود الوظيفة والمعرف بعد الانضمام إلى النجاح أولا؟ سيوفر لك الكثير من الوقت.

ولكن فقط تعرف الصورة الأكبر لذلك كل ما كتبته هنا قد يكون القمامة ؛-)

على أي حال، آمل أن أساعدتك بطريقة ما.

نصائح أخرى

أعتقد أن SQL Server يستدعي وظيفتك get_servicesbyclientidandtype مرة واحدة لكل مجموعة من قيم المعلمة ولكن ذلك لكل صف في جدول العملاء. لديك ثلاث مجموعات من القيم، لذلك بالنسبة إلى 100 صفوف في جدول العميل، قد ترى 300 مكالمة من الوظيفة.

ولكن لتكون واثقا، قم بتشغيل الاستعلام في SQL Server Management Studio والتبديل على خيار "إظهار خطة التنفيذ". وبهذه الطريقة، يمكنك اكتشاف أي جزء من استفسارك يستهلك أكبر الموارد وأجهده على تحسين هذا الجزء.

شيء واحد يجب مراعاته هو تجنب "لا" إذا كان ذلك ممكنا. "لا" غير قابل للشفقة، لن تكون قادرا على اتخاذ مزايا كاملة للفهرسة. للوهلة الأولى، لا أرى طريقة لإعادة كتابةها لتجنب التعبيرات غير المعروضة. FWIW، YMMV. :-)

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