문제

"모범 사례" 중 하나는 저장 프로시저를 통해 데이터에 액세스하는 것입니다.이 시나리오가 왜 좋은지 이해합니다.내 동기는 데이터베이스와 애플리케이션 논리를 분할하는 것(저장 프로시저의 동작이 동일한 경우 테이블을 변경할 수 있음), SQL 주입에 대한 방어(사용자는 "select * from 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

그리고 "읽기"를 위한 마지막 저장 프로시저는 저에게 약간 미스터리입니다.복잡한 조건에 대해 필터 값을 어떻게 전달합니까?몇 가지 제안이 있습니다:

where 조건 전달을 위해 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

하지만 이 제안은 조금 복잡하다고 생각합니다.

이러한 유형의 저장 프로시저를 수행하는 "모범 사례"가 있습니까?

도움이 되었습니까?

해결책

첫 번째:삭제 루틴의 경우 where 절에는 기본 키만 포함되어야 합니다.

두번째:업데이트 루틴의 경우 작업 코드가 있기 전에 최적화를 시도하지 마십시오.실제로 애플리케이션을 프로파일링하고 병목 현상이 있는 위치를 확인할 수 있을 때까지는 최적화를 시도하지 마십시오.한 행의 한 열을 업데이트하는 것과 한 행의 모든 ​​열을 업데이트하는 속도는 거의 동일하다는 것을 확실히 말씀드릴 수 있습니다.DBMS에서 시간이 걸리는 것은 (1) 데이터를 쓸 디스크 블록을 찾는 것과 (2) 쓰기가 일관되도록 다른 기록기를 잠그는 것입니다.마지막으로 변경이 필요한 열만 업데이트하는 데 필요한 코드를 작성하는 것은 일반적으로 수행하기 어렵고 유지 관리하기도 더 어렵습니다.정말로 까다로워지고 싶다면 모든 열을 업데이트하는 것과 비교하여 어떤 열이 변경되었는지 알아내는 속도를 비교해야 합니다.모두 업데이트하면 그 중 어느 것도 읽을 필요가 없습니다.

제삼:나는 각 검색 경로에 대해 하나의 저장 프로시저를 작성하는 경향이 있습니다.귀하의 예에서는 기본 키별로 하나씩, 각 외래 키별로 하나씩 만든 다음 애플리케이션에서 필요에 따라 새 액세스 경로마다 하나씩 추가합니다.민첩하게 행동하세요.필요하지 않은 코드를 작성하지 마십시오.또한 저장 프로시저 대신 뷰를 사용하는 데 동의합니다. 하지만 저장 프로시저를 사용하여 여러 결과 집합(일부 MSSQL 버전에서는)을 반환하거나 행을 열로 변경할 수 있으므로 유용할 수 있습니다.

예를 들어 기본 키로 7개 행을 가져와야 하는 경우 몇 가지 옵션이 있습니다.기본 키로 한 행을 가져오는 저장 프로시저를 7번 호출할 수 있습니다.모든 통화 사이에 연결을 열어두면 충분히 빠를 수 있습니다.한 번에 특정 수(예: 10개) 이상의 ID가 필요하지 않다는 것을 알고 있는 경우 "및 ID in (arg1, arg2, arg3...)"과 같은 where 절을 포함하는 저장 프로시저를 작성하고 사용하지 않는 인수는 NULL로 설정되어 있는지 확인하세요.동적 SQL을 생성해야 한다고 결정한 경우 TSQL은 다른 언어만큼 실수하기 쉽기 때문에 저장 프로시저를 사용하지 않아도 됩니다.또한 데이터베이스를 사용하여 문자열 조작을 수행해도 아무런 이점이 없습니다. 거의 항상 병목 현상이 발생하므로 DB에 필요한 것보다 더 많은 작업을 제공할 필요가 없습니다.

다른 팁

데이터를 읽으려면 보안을 위해 저장된 절차가 필요하지 않거나 논리를 분리하려면 뷰를 사용할 수 있습니다.

보기에서만 선택하십시오.

표시된 레코드를 제한하고, 필드 이름을 변경하고, 많은 테이블을 하나의 논리 "테이블"에 연결할 수 있습니다.

삽입/업데이트/선택 저장 프로 시저가 "모범 사례"라는 것은 동의하지 않습니다. 전체 응용 프로그램이 SPS로 작성되지 않는 한 응용 프로그램에 데이터베이스 계층을 사용하여 이러한 CRUD 활동을 처리하십시오. 더 나은 방법은 ORM 기술을 사용하여 처리합니다.

내 제안은 당신이 지금 또는 필요한 모든 것을 수행하는 저장된 절차를 만들려고하지 않는다는 것입니다. 테이블의 기본 키를 기반으로 행을 검색 해야하는 경우 저장된 절차를 작성하여이를 수행하십시오. 일련의 기준을 충족하는 모든 행을 검색 해야하는 경우 해당 기준이 무엇인지 알아 내고 저장된 절차를 작성하십시오.

특정 문제가 아닌 모든 문제를 해결하는 소프트웨어를 작성하려고하면 일반적으로 유용한 것을 제공하는 데 실패합니다.

선택 저장된 절차는 하나의 저장된 Proc이지만 WHERE 절에 다른 수의 다른 항목만을 요구하기 위해 다음과 같이 수행 할 수 있습니다. 매개 변수의 하나 또는 조합을 전달하면 일치하는 모든 항목을 얻을 수 있으므로 저장된 Proc 만 있으면됩니다.

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에서는 2G 제한이 있지만 NVARCHAR (NVARCHAR)에 대해 거의 모든 문자열 작업을 수용하는 NVARCHAR (MAX)를 지원합니다. 이것이 첫 번째 접근 방식에서 필요한 것에 맞는지 테스트 할 수 있습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top