Frage

Einer der „best practice“ werden Daten über gespeicherte Prozeduren zugreifen. Ich verstehe, warum ist dieses Szenario gut. Meine Motivation ist geteilt Datenbank und Anwendungslogik (die Tabellen kann mir geändert, wenn das Verhalten der gespeicherten Prozeduren gleich sind), Verteidigung für SQL-Injection (Benutzer kann nicht ausgeführt werden „select * from some_tables“, können sie nur gespeicherte Prozeduren nennen), und Sicherheit (in gespeicherte Prozedur kann „alles“ sein, die der Benutzer zu sichern, kann nicht wählen / insert / update / löschen von Daten, die nicht für sich ist).

Was ich nicht weiß, ist, wie Daten mit dynamischen Filtern zugreifen zu können.

Ich bin mit MSSQL 2005.

Wenn ich Tabelle:

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)
)

dann sollte ich 4 gespeicherte Prozeduren erstellen (create / lesen / update / delete).

Die gespeicherte Prozedur für "create" ist einfach.

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

Die gespeicherte Prozedur für „Löschen“ ist zu einfach.

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

Die gespeicherte Prozedur für „update“ ist ähnlich wie bei „löschen“, aber ich bin nicht sicher, ob dies der richtige Weg ist, wie es zu tun. Ich denke, dass alle Spalten der Aktualisierung nicht effizient ist.

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

Und die letzte - gespeicherte Prozedur für „lesen“ ist für mich LITTLEBIT Geheimnis. Wie Filterwerte für komplexe Bedingungen passieren? Ich habe ein paar Vorschlag:

Verwenden von XML-Parameter für das Bestehen in dem die Bedingung:

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

Aber ich denke, die Einschränkung von „4000“ Zeichen für eine Abfrage hässlich ist.

Der nächste Vorschlag wird mit Filtertabellen für jede Spalte. Legen Sie Filterwerte in der Filtertabelle und dann rufen gespeicherte Prozedur mit ID von Filtern:

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

Aber dieser Vorschlag ist littlebit kompliziert denke ich.

Gibt es ein „best practice“ diese Art von gespeicherten Prozeduren zu tun?

War es hilfreich?

Lösung

Erstens:. Für die Löschroutine, Ihre where-Klausel sollte nur den Primärschlüssel enthalten

Zweitens: für Ihre Update-Routine, versuchen Sie nicht, zu optimieren, bevor Sie Code gearbeitet haben. In der Tat, versuchen Sie nicht, zu optimieren, bis Sie Ihre Anwendung profilieren können und sehen, wo die Engpässe sind. Ich kann Ihnen sicher sagen, dass eine Spalte von einer Zeile zu aktualisieren und alle Spalten einer Zeile Aktualisierung in Geschwindigkeit nahezu identisch ist. Was Zeit in einem DBMS nimmt, ist (1) Suche nach dem Plattenblock, wo Sie die Daten schreiben werden und (2) Aussperren andere Autoren, so dass Ihr Schreib konsistent sein wird. Schließlich schreibt den Code notwendig, nur die Spalten zu aktualisieren, die im Allgemeinen ändern müssen wird schwieriger zu tun und schwieriger zu pflegen. Wenn Sie wirklich bekommen pingelig wollte, würde man die Geschwindigkeit herauszufinden, welche Spalten verändert verglichen mit nur Aktualisierung jede Spalte vergleichen müssen. Wenn Sie sie alle aktualisieren, müssen Sie sich nicht von ihnen lesen.

Drittens: Ich neige dazu, eine gespeicherte Prozedur für jeden Abruf Pfad zu schreiben. In Ihrem Beispiel würde ich einen nach dem Primärschlüssel machen, eine von jedem Fremdschlüssel und dann würde ich einen für jeden neuen Zugriffspfad hinzufügen, wie ich sie in der Anwendung benötigt. Agil; keinen Code schreiben Sie nicht brauchen. Ich stimme auch mit Blick anstelle von gespeicherten Prozeduren verwenden, jedoch können Sie eine gespeicherte Prozedur verwenden, um mehrere Ergebnismengen zurückgeben (in einigen Versionen von MSSQL) oder Zeilen in Spalten zu ändern, was nützlich sein können.

Wenn Sie erhalten müssen, beispielsweise 7 Reihen von Primärschlüssel haben Sie einige Optionen. Sie können die gespeicherte Prozedur aufrufen, die eine Reihe von Primärschlüssel siebenmal bekommt. Dies kann schnell genug sein, wenn Sie die Verbindung zu halten zwischen allen Anrufen geöffnet. Wenn Sie wissen, dass Sie nie mehr als eine bestimmte Anzahl benötigen (etwa 10) von IDs zu einer Zeit, können Sie eine gespeicherte Prozedur schreiben, die eine where-Klausel wie „und ID in (arg1, arg2, arg3 ...)“ enthält und machen Sie sicher, dass nicht verwendetes Argument auf NULL gesetzt. Wenn Sie sich entscheiden, müssen Sie dynamische SQL generieren, würde ich nicht mit einer gespeicherten Prozedur stören, weil TSQL genauso einfach ist, einen Fehler wie jede andere Sprache zu machen. Auch Sie keinen Nutzen Gewinn aus der Datenbank mit String-Manipulation zu tun -. Es ist fast immer Ihr Engpass, so gibt es keinen Grund, die DB mehr Arbeit als nötig geben

Andere Tipps

zum Lesen von Daten, die Sie nicht eine gespeicherte Prozedur für die Sicherheit benötigen oder Logik zu trennen, können Sie Ansichten verwenden.

Just gewähren wählt nur auf der Ansicht.

können Sie begrenzen die Datensätze angezeigt, Namen ändern Feld, verbinden viele Tabellen zu einer logischen „Tabelle“, etc.

Ich bin nicht einverstanden, dass Insert / Update / Wählen Sie gespeicherte Prozeduren sind ein "best practice" erstellen. Es sei denn, Ihre gesamte Anwendung wird in SPs geschrieben, eine Datenbankschicht in Ihrer Anwendung verwenden, um diese CRUD Aktivitäten zu behandeln. Noch besser wäre es, eine ORM-Technologie verwendet sie für Sie zu behandeln.

Mein Vorschlag ist, dass Sie nicht versuchen, eine gespeicherte Prozedur zu erstellen, die alles tut, die Sie vielleicht jetzt oder jemals tun müssen. Wenn Sie eine Zeile auf dem Tisch des Primärschlüssel basiert abrufen müssen dann eine gespeicherte Prozedur schreiben, das zu tun. Wenn Sie für alle Zeilen suchen, müssen eine Reihe von Kriterien erfüllen, dann herauszufinden, was die Kriterien sein könnten und eine gespeicherte Prozedur schreiben, das zu tun.

Wenn Sie versuchen, Software zu schreiben, die Sie auf die Bereitstellung etwas Nützliches scheitern wird in der Regel jedes mögliches Problem, anstatt eine Reihe von spezifischen Probleme löst.

Ihr wählen gespeicherte Prozedur kann getan werden, da nur eine gespeicherte Prozedur zu verlangen, folgt aber eine beliebige Anzahl verschiedener Elemente in der where-Klausel. Der Pass in irgendeiner oder einer Kombination der Parameter und es werden alle Einzelteile erhalten, die passen -. So benötigen Sie nur eine gespeicherte Prozedur

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))

In SQL 2005 unterstützt es nvarchar (max), die eine Grenze von 2G, aber praktisch alle String-Operationen auf normale nvarchar zu akzeptieren. Sie können testen wollen, ob dies in passen, was man im ersten Ansatz muß.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top