سؤال

واحدة من "أفضل الممارسات" يتم الوصول إلى البيانات عبر الإجراءات المخزنة. وأنا أفهم لماذا هذا السيناريو جيدة. حافزي هو قاعدة بيانات تقسيم ومنطق التطبيق (الجداول يمكن لي تغيرت، وإذا كان سلوك الإجراءات المخزنة هي نفسها)، والدفاع عن حقن SQL (لا يمكن للمستخدمين تنفيذ "حدد * من some_tables"، لا يمكن إلا أن استدعاء الإجراءات المخزنة)، و الأمن (في الإجراء المخزن يمكن أن يكون "أي شيء" وتأمين، التي لا يمكن للمستخدم اختيار / إدراج / تحديث / حذف البيانات، وهي ليست لهم).

وما لا نعرفه هو كيفية الوصول إلى البيانات مع المرشحات الحيوية.

وأنا باستخدام MSSQL 2005.

وإذا كان لدي جدول:

CREATE TABLE tblProduct (
   ProductID uniqueidentifier -- PK
   , IDProductType uniqueidentifier -- FK to another table
   , ProductName nvarchar(255) -- name of product
   , ProductCode nvarchar(50) -- code of product for quick search
   , Weight decimal(18,4)
   , Volume decimal(18,4)
)

وبعد ذلك يجب إنشاء 4 الإجراءات المخزنة (إنشاء / قراءة / تحديث / حذف).

والإجراء المخزن ل "خلق" سيكون أمرا سهلا.

CREATE PROC Insert_Product ( @ProductID uniqueidentifier, @IDProductType uniqueidentifier, ... etc ... ) AS BEGIN
   INSERT INTO tblProduct ( ProductID, IDProductType, ... etc .. ) VALUES ( @ProductID, @IDProductType, ... etc ... )
END

والإجراء المخزن ل "حذف" من السهل للغاية.

CREATE PROC Delete_Product ( @ProductID uniqueidentifier, @IDProductType uniqueidentifier, ... etc ... ) AS BEGIN
    DELETE tblProduct WHERE ProductID = @ProductID AND IDProductType = @IDProductType AND ... etc ...
END

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

CREATE PROC Update_Product( @ProductID uniqueidentifier, @Original_ProductID uniqueidentifier, @IDProductType uniqueidentifier, @Original_IDProductType uniqueidentifier, ... etc ... ) AS BEGIN
   UPDATE tblProduct SET ProductID = @ProductID, IDProductType = @IDProductType, ... etc ...
      WHERE ProductID = @Original_ProductID AND IDProductType = @Original_IDProductType AND ... etc ...
END

والاخيرة - الإجراء المخزن ل "اقرأ" وlittlebit الغموض بالنسبة لي. كيف تمرير قيم التصفية لالظروف المعقدة؟ لدي عدد قليل اقتراح:

<ع> استخدام المعلمة XML لتمرير حيث الشرط:

CREATE PROC Read_Product ( @WhereCondition XML ) AS BEGIN
    DECLARE @SELECT nvarchar(4000)
    SET @SELECT = 'SELECT ProductID, IDProductType, ProductName, ProductCode, Weight, Volume FROM tblProduct'

    DECLARE @WHERE nvarchar(4000)
    SET @WHERE = dbo.CreateSqlWherecondition( @WhereCondition ) --dbo.CreateSqlWherecondition is some function which returns text with WHERE condition from passed XML

    DECLARE @LEN_SELECT int
    SET @LEN_SELECT = LEN( @SELECT )
    DECLARE @LEN_WHERE int
    SET @LEN_WHERE = LEN( @WHERE )
    DECLARE @LEN_TOTAL int
    SET @LEN_TOTAL = @LEN_SELECT + @LEN_WHERE
    IF @LEN_TOTAL > 4000 BEGIN
        -- RAISE SOME CONCRETE ERROR, BECAUSE DYNAMIC SQL ACCEPTS MAX 4000 chars
    END

    DECLARE @SQL nvarchar(4000)
    SET @SQL = @SELECT + @WHERE

    EXEC sp_execsql @SQL
END

ولكن، أعتقد أن الحد من "4000" أحرف الاستعلام واحد هو قبيح.

والاقتراح المقبل يستخدم الجداول تصفية كل عمود. إدراج قيم التصفية في الجدول مرشح ومن ثم استدعاء الإجراء المخزن مع ID من الفلاتر:

CREATE TABLE tblFilter (
   PKID uniqueidentifier -- PK
   , IDFilter uniqueidentifier -- identification of filter
   , FilterType tinyint -- 0 = ignore, 1 = equals, 2 = not equals, 3 = greater than, etc ...
   , BitValue bit , TinyIntValue tinyint , SmallIntValue smallint, IntValue int
   , BigIntValue bigint, DecimalValue decimal(19,4), NVarCharValue nvarchar(4000)
   , GuidValue uniqueidentifier, etc ... )

CREATE TABLE Read_Product ( @Filter_ProductID uniqueidentifier, @Filter_IDProductType uniqueidentifier, @Filter_ProductName uniqueidentifier, ... etc ... ) AS BEGIN
   SELECT ProductID, IDProductType, ProductName, ProductCode, Weight, Volume
   FROM tblProduct
   WHERE ( @Filter_ProductID IS NULL
            OR ( ( ProductID IN ( SELECT GuidValue FROM tblFilter WHERE IDFilter = @Filter_ProductID AND FilterType = 1 ) AND NOT ( ProductID IN ( SELECT GuidValue FROM tblFilter WHERE IDFilter = @Filter_ProductID AND FilterType = 2 ) )
      AND ( @Filter_IDProductType IS NULL
            OR ( ( IDProductType IN ( SELECT GuidValue FROM tblFilter WHERE IDFilter = @Filter_IDProductType AND FilterType = 1 ) AND NOT ( IDProductType IN ( SELECT GuidValue FROM tblFilter WHERE IDFilter = @Filter_IDProductType AND FilterType = 2 ) )
      AND ( @Filter_ProductName IS NULL OR ( ... etc ... ) ) 
END

ولكن هذا الاقتراح معقد littlebit على ما أعتقد.

هل هناك "أفضل الممارسات" للقيام بهذا النوع من الإجراءات المخزنة؟

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

المحلول

أولا: لروتين حذف الخاص، حيث يجب أن تتضمن بندا فقط المفتاح الأساسي

وثانيا: لروتين التحديث، لا في محاولة لتحسين قبل لديك التعليمات البرمجية العمل. في الواقع، لا في محاولة لتحسين حتى تتمكن من ملف التطبيق الخاص بك ونرى أين هي الاختناقات. استطيع ان اقول لكم لعلى يقين أن تحديث عمود واحد من صف واحد وتحديث كافة الأعمدة من صف واحد متطابقة تقريبا في سرعة. ما يستغرق وقتا طويلا في DBMS هو (1) العثور على كتلة القرص حيث ستتمكن من كتابة البيانات و (2) قفل من الكتاب الآخرين بحيث الكتابة الخاصة بك وسوف تكون متسقة. وأخيرا، كتابة التعليمات البرمجية الضرورية لتحديث فقط الأعمدة التي تحتاج إلى تغيير عموما سيكون من الصعب القيام به وأصعب للحفاظ عليها. إذا كنت تريد حقا أن تحصل من الصعب إرضاءه، وكنت قد لمقارنة سرعة معرفة الأعمدة التي تغيرت مقارنة مع مجرد استكمال كل عمود. إذا قمت بتحديث كل منهم، لم يكن لديك لقراءة أي منها.

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

إذا كنت بحاجة للحصول، على سبيل المثال، 7 صفوف من المفتاح الأساسي، لديك بعض الخيارات. يمكنك استدعاء الإجراء المخزن أن يحصل على صف واحد من الابتدائية سبع مرات الرئيسية. قد يكون هذا الصوم بما فيه الكفاية إذا كنت تحتفظ اتصال فتحت بين جميع المكالمات. إذا كنت تعرف أنك لن تحتاج إلى أكثر من عدد معين (مثلا 10) من معرفات في وقت واحد، يمكنك كتابة الإجراء المخزن الذي يتضمن بندا حيث مثل "وID في (ARG1، ARG2، arg3 ...)" وجعل تأكد من أن يتم تعيين الحجج غير المستخدمة إلى NULL. إذا قررت أنك بحاجة لتوليد SQL الحيوي، وأود أن لا يكلف نفسه عناء مع إجراء مخزن لTSQL تماما كما هو من السهل أن تجعل من الخطأ مثل أي لغة أخرى. أيضا، يمكنك الحصول على أي فائدة من استخدام قاعدة البيانات للقيام التلاعب سلسلة - انها دائما تقريبا عنق الزجاجة الخاصة بك، لذلك ليس هناك نقطة في إعطاء DB أي عمل أكثر من اللازم

نصائح أخرى

لقراءة البيانات، لا تحتاج إجراء مخزن للأمن أو لفصل المنطق، يمكنك استخدام وجهات النظر.

ومجرد منحة حدد فقط في طريقة العرض.

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

وأنا لا أوافق أن إنشاء إدراج / تحديث / تحديد الإجراءات المخزنة هي "أفضل الممارسات". ما لم يتم كتابة التطبيق بأكمله في الصحة والصحة النباتية، واستخدام طبقة قاعدة البيانات في التطبيق الخاص بك للتعامل مع هذه الأنشطة CRUD. والأفضل من ذلك، استخدام التكنولوجيا ORM التعامل معها بالنسبة لك.

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

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

ويمكن أن يتم إجراء مخزن حدد على النحو التالي تتطلب واحد فقط بروك المخزنة لكن أي عدد من العناصر المختلفة في جملة WHERE. تمر في أي واحد أو مجموعة من المعلمات وستحصل على جميع العناصر التي تتطابق - ذلك ما عليك سوى واحد بروك المخزنة

Create sp_ProductSelect
(
 @ProductID int = null,
 @IDProductType int = null,
 @ProductName varchar(50) = null,
 @ProductCode varchar(10) = null,
 ...
 @Volume int = null
)
AS
SELECT ProductID, IDProductType, ProductName, ProductCode, Weight, Volume FROM tblProduct'  
Where
  ((@ProductID is null) or (ProductID = @ProductID)) AND
  ((@ProductName is null) or (ProductName = @ProductName)) AND
  ...
  ((@Volume is null) or (Volume= @Volume))

في SQL 2005، فإنه يدعم NVARCHAR (حد أقصى)، والتي لديها حدود 2G، ولكن قبول تقريبا كل عمليات سلسلة على NVARCHAR العادي. قد تحتاج إلى اختبار ما إذا كان هذا يمكن أن تندرج في ما تحتاج إليه في النهج الأول.

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