다음을 통해 공유


T-SQL 디자인 문제

적용 대상: Microsoft Fabric의 SQL ServerAzure SQL Database Azure SQL Managed Instance SQL 데이터베이스

데이터베이스 프로젝트에서 T-SQL 코드를 분석할 때 하나 이상의 경고가 디자인 문제로 분류될 수 있습니다. 다음 상황을 방지하려면 디자인 문제를 해결해야 합니다.

  • 이후에 데이터베이스를 변경하면 데이터베이스에 의존하는 애플리케이션이 중단될 수 있습니다.
  • 코드에서 예상된 결과를 생성하지 못할 수 있습니다.
  • SQL Server의 이후 릴리스와 함께 실행하면 코드가 중단될 수 있습니다.

일반적으로 현재 또는 나중에 애플리케이션이 중단될 수 있으므로 디자인 문제를 표시하지 않아야 합니다.

제공된 규칙은 다음과 같은 디자인 문제를 식별합니다.

SR0001: 저장 프로시저, 뷰 및 테이블 반환 함수에서 SELECT * 방지

저장 프로시저, 뷰 또는 테이블 반환 함수에서 와일드카드 문자를 사용하여 테이블 또는 뷰의 모든 열을 선택하는 경우 기본 테이블 또는 뷰가 변경될 경우 반환된 열의 개수 또는 모양이 변경될 수 있습니다. 열의 모양은 해당 형식과 크기의 조합입니다. 이러한 차이로 인해 저장 프로시저, 뷰 또는 테이블 반환 함수를 사용하는 애플리케이션에서 문제가 발생할 수 있습니다. 이러한 소비자는 다른 수의 열을 예상하기 때문입니다.

위반 문제를 해결하는 방법

와일드카드 문자를 정규화된 열 이름 목록으로 바꿔서 저장 프로시저, 뷰 또는 테이블 반환 함수의 소비자를 스키마 변경으로부터 보호할 수 있습니다.

예시

다음 예제에서는 먼저 [Table2]라는 테이블을 정의한 다음 두 개의 저장 프로시저를 정의합니다. 첫 번째 프로시저는 규칙 SR0001을 위반하는 SELECT *을(를) 포함합니다. 두 번째 절차에서는 SELECT *을(를) 피하고 SELECT 문에 열을 명시적으로 나열합니다.

CREATE TABLE [dbo].[Table2]
(
[ID] INT NOT NULL IDENTITY(0, 1),
[c1] INT NOT NULL,
[Comment] NVARCHAR (50)
)
ON [PRIMARY]

CREATE PROCEDURE [dbo].[procWithWarning]
AS
BEGIN
-- Contains code that breaks rule SR0001
SELECT *
FROM [dbo].[Table2]
END

CREATE PROCEDURE [dbo].[procFixed]
AS
BEGIN
-- Explicitly lists the column names in a SELECT statement
SELECT [dbo].[Table2].[ID], [dbo].[Table2].[c1], [dbo].[Table2].[Comment]
FROM [dbo].[Table2]
END

SR0008: @@IDENTITY 대신 SCOPE_IDENTITY 사용 고려

@@IDENTITY는 전역 ID 값이므로 현재 범위 외부에서 업데이트되어 예기치 않은 값을 얻었을 수 있습니다. 복제에 사용되는 중첩된 트리거를 포함한 트리거는 현재 범위 외부의 @@IDENTITY를 업데이트할 수 있습니다.

위반 문제를 해결하는 방법

이 문제를 해결하려면 @@IDENTITY에 대한 참조를 사용자 문의 범위에서 가장 최근의 ID 값을 반환하는 SCOPE_IDENTITY로 바꿔야 합니다.

예시

첫 번째 예제에서 @@IDENTITY 테이블에 데이터를 삽입하는 저장 프로시저에 사용됩니다. 그런 다음 병합 복제를 위해 테이블이 게시되어 게시된 테이블에 트리거가 추가됩니다. 따라서 @@IDENTITY는 사용자 테이블에 대한 삽입 오퍼레이션 대신 복제 시스템 테이블에 대한 삽입 오퍼레이션에서 값을 반환할 수 있습니다.

Sales.Customer 테이블의 최대 ID 값은 29483입니다. 테이블에 행을 삽입하면 @@IDENTITY 및 SCOPE_IDENTITY()에서 서로 다른 값을 반환합니다. SCOPE_IDENTITY()는 사용자 테이블에 대한 삽입 오퍼레이션에서 값을 반환하는 반면 @@IDENTITY는 복제 시스템 테이블에 대한 삽입 오퍼레이션에서 값을 반환합니다.

두 번째 예제에서는 SCOPE_IDENTITY()를 사용하여 삽입된 ID 값에 액세스하고 경고를 해결하는 방법을 보여 줍니다.

CREATE PROCEDURE [dbo].[ProcedureWithWarning]
@param1 INT,
@param2 NCHAR(1),
@Param3 INT OUTPUT
AS
BEGIN
INSERT INTO Sales.Customer ([TerritoryID],[CustomerType]) VALUES (@param1,@param2);

SELECT @Param3 = @@IDENTITY
END

CREATE PROCEDURE [dbo].[ProcedureFixed]
@param1 INT,
@param2 NCHAR(1),
@param3 INT OUTPUT
AS
BEGIN
INSERT INTO Sales.Customer ([TerritoryID],[CustomerType]) VALUES (@param1,@param2);

SELECT @Param3 = SCOPE_IDENTITY()
END

SR0009: 크기가 1 또는 2인 가변 길이 형식 사용 방지

VARCHAR, NVARCHAR 및 VARBINARY와 같은 가변 길이의 데이터 형식을 사용하는 경우 데이터 형식에 저장된 값의 길이를 추적하기 위해 추가 스토리지 비용이 발생합니다. 또한 가변 길이의 열은 고정 길이의 모든 열 후에 저장되며 성능에 영향을 줄 수 있습니다. VARCHAR와 같은 가변 길이 형식을 선언하는 경우에도 경고가 표시되지만 길이는 지정하지 않습니다. 이 경고는 지정되지 않은 경우 기본 길이가 1이기 때문에 발생합니다.

위반 문제를 해결하는 방법

형식의 길이가 매우 작고(크기 1 또는 2) 일관성이 있는 경우 CHAR, NCHAR 및 BINARY와 같은 고정 길이 형식으로 선언합니다.

예시

이 예제에서는 두 테이블에 대한 정의를 보여 줍니다. 첫 번째 테이블은 길이가 2인 가변 길이 문자열을 선언합니다. 두 번째 테이블은 대신 고정 길이 문자열을 선언하여 경고를 방지합니다.

CREATE TABLE [dbo].[TableWithWarning]
(
[ID] INT NOT NULL IDENTITY(0, 1),
[c1] INT NOT NULL PRIMARY KEY,
[c2] INT,
[c3] INT,
[SmallString] VARCHAR(2)
)
ON [PRIMARY]

CREATE TABLE [dbo].[FixedTable]
(
[ID] INT NOT NULL IDENTITY(0, 1),
[c1] INT NOT NULL PRIMARY KEY,
[c2] INT,
[c3] INT,
[SmallString] CHAR(2)
)
ON [PRIMARY]

가변 길이 형식에 대한 데이터는 고정 길이 형식의 데이터 후에 물리적으로 저장됩니다. 따라서 열이 비어 있지 않은 테이블에서 고정 길이로 열을 변경하는 경우 데이터 이동이 발생합니다.

SR0010: 테이블 또는 뷰를 조인할 때 사용되지 않는 구문 사용 방지

사용되지 않는 구문을 사용하는 조인은 다음 두 가지 범주로 나타집니다.

  • 내부 조인: 내부 조인의 경우 조인되는 열의 값은 =, <, >= 등의 비교 연산자를 사용하여 비교됩니다. 내부 조인은 각 테이블에서 하나 이상의 행이 조인 조건과 일치하는 경우에만 행을 반환합니다.
  • 외부 조인: 외부 조인은 FROM 절에 지정된 하나 이상의 테이블이나 뷰에서 WHERE 또는 HAVING 검색 조건을 만족하는 모든 행을 반환합니다. = 또는 =를 사용하여 외부 조인을 지정하는 경우, 사용되지 않음 구문을 사용합니다.

위반 문제를 해결하는 방법

내부 조인에서 위반 문제를 해결하려면 INNER JOIN 구문을 사용합니다 .

외부 조인의 위반 문제를 해결하려면 적절한 OUTER JOIN 구문을 사용합니다. 다음과 같은 옵션이 있습니다.

  • LEFT OUTER JOIN 또는 LEFT JOIN
  • RIGHT OUTER JOIN 또는 RIGHT JOIN

사용되지 않는 구문 및 업데이트된 구문의 예는 다음 예제에서 제공됩니다. 자세한 내용은 조인에서 확인할 수 있습니다.

예제

여섯 가지 예제는 다음 옵션을 보여 줍니다.

  1. 예제 1에서는 내부 조인에 대해 사용되지 않는 구문을 보여 줍니다.
  2. 예제 2에서는 예제 1을 업데이트하여 현재 구문을 사용하는 방법을 보여 줍니다.
  3. 예제 3에서는 왼쪽 외부 조인에 대해 사용되지 않는 구문을 보여 줍니다.
  4. 예제 4에서는 예제 2를 업데이트하여 현재 구문을 사용하는 방법을 보여 줍니다.
  5. 예제 5에서는 오른쪽 외부 조인에 대해 사용되지 않는 구문을 보여 줍니다.
  6. 예제 6에서는 예제 5를 업데이트하여 현재 구문을 사용하는 방법을 보여 줍니다.
-- Example 1: Deprecated syntax for an inner join
SELECT [T2].[c3], [T1].[c3]
FROM [dbo].[Table2] T2, [dbo].[Table1] T1
WHERE [T1].[ID] = [T2].[ID]

-- Example 2: Current syntax for an inner join
SELECT [T2].[c3], [T1].[c3]
FROM [dbo].[Table2] AS T2
INNER JOIN [dbo].[Table1] as T1
ON [T2].[ID] = [T2].[ID]

-- Example 3: Deprecated syntax for a left outer join
SELECT [T2].[c3], [T1].[c3]
FROM [dbo].[Table2] T2, [dbo].[Table1] T1
WHERE [T1].[ID] *= [T2].[ID]

-- Example 4: Fixed syntax for a left outer join
SELECT [T2].[c3], [T1].[c3]
FROM [dbo].[Table2] AS T2
LEFT OUTER JOIN [dbo].[Table1] as T1
ON [T2].[ID] = [T2].[ID]

-- Example 5: Deprecated syntax for a right outer join
SELECT [T2].[c3], [T1].[c3]
FROM [dbo].[Table2] T2, [dbo].[Table1] T1
WHERE [T1].[ID] =* [T2].[ID]

-- Example 6: Fixed syntax for a right outer join
SELECT [T2].[c3], [T1].[c3]
FROM [dbo].[Table2] AS T2
RIGHT OUTER JOIN [dbo].[Table1] as T1
ON [T2].[ID] = [T2].[ID]

SR0013: 출력 매개 변수(파라미터)가 모든 코드 경로에 채워지지 않음

이 규칙은 출력 매개 변수가 저장 프로시저 또는 함수를 통해 하나 이상의 코드 경로에 있는 값으로 설정되지 않은 코드를 식별합니다. 이 규칙은 출력 매개 변수를 설정해야 하는 경로를 식별하지 않습니다. 여러 출력 매개 변수에 이 문제가 있는 경우 각 매개 변수에 대해 하나의 경고가 나타납니다.

위반 문제를 해결하는 방법

다음 두 가지 방법 중 하나로 문제를 해결할 수 있습니다. 프로시저 본문의 시작 부분에 있는 기본값으로 출력 매개 변수를 초기화하는 경우 이 문제를 가장 쉽게 해결할 수 있습니다. 또는 출력 매개 변수를 매개 변수가 설정되지 않은 특정 코드 경로의 값으로 설정할 수도 있습니다. 그러나 복잡한 프로시저에서는 일반적이지 않은 코드 경로를 간과할 수 있습니다.

Important

CREATE PROC MyProcedure (@param1 INT = 10 OUTPUT)와(과) 같은 프로시저 선언 내에서 값을 지정하면 문제가 해결되지 않습니다. 프로시저 본문 내의 출력 매개 변수에 값을 할당해야 합니다.

예시

다음 예제에서는 두 가지 간단한 프로시저를 보여 줍니다. 첫 번째 프로시저는 출력 매개 변수 @Sum의 값을 설정하지 않습니다. 두 번째 프로시저는 프로시저 시작 시 @Sum 매개 변수를 초기화하여 값이 모든 코드 경로에 설정되도록 합니다.

CREATE PROCEDURE [dbo].[procedureHasWarning]
(
@Value1 BIGINT,
@Value2 INT,
@Value3 INT,
@Sum INT OUTPUT
)
AS
BEGIN
-- No initialization of the output parameter
--
-- Additional statements here.
--
RETURN 0;
END
--
CREATE PROCEDURE [dbo].[procedureFixed]
(
@Value1 BIGINT,
@Value2 INT,
@Value3 INT,
@Sum INT OUTPUT
)
AS
BEGIN
-- Initialize the out parameter
SET @Sum = 0;
--
-- Additional statements here
--
RETURN 0;
END

SR0014: {Type1}에서 {Type2}로 캐스팅할 때 데이터 손실이 발생할 수 있음

데이터 형식이 열, 변수 또는 매개 변수에 일관되지 않게 할당된 경우 해당 개체를 포함하는 Transact-SQL 코드가 실행되면 암시적으로 변환됩니다. 이러한 유형의 변환은 성능을 저하시킬 뿐만 아니라 경우에 따라 데이터가 약간 손실되는 경우도 있습니다. 예를 들어 WHERE 절의 모든 열을 변환해야 하는 경우 테이블 검색이 실행될 수 있습니다. 더 나쁜 경우, 유니코드 문자열이 다른 코드 페이지를 사용하는 ASCII 문자열로 변환되는 경우 데이터가 손실될 수 있습니다.

이 규칙은 다음을 수행하지 않습니다.

  • 런타임까지 형식을 알 수 없으므로 계산 열의 형식을 확인합니다.
  • CASE 문 내의 모든 항목을 분석합니다. CASE 문의 반환 값도 분석하지 않습니다.
  • ISNULL 호출의 입력 매개 변수 또는 반환 값 분석

이 표에는 규칙 SR0014에서 다루는 검사가 요약되어 있습니다.

언어 구문확인된 내용예시
매개 변수의 기본값매개 변수 데이터 형식
CREATE PROCEDURE p1(@p1 INT = 1)
AS
BEGIN
END
CREATE INDEX 조건자조건자는 부울입니다.
CREATE INDEX index1 ON table1 (column1)
WHERE column1 > 10
LEFT 또는 RIGHT 함수의 인수문자열 인수 형식 및 길이
SET @v = LEFT('abc', 2)
CAST 및 CONVERT 함수의 인수식 및 형식이 유효합니다.
SET @v = CAST('abc' AS CHAR(10))
SET 문왼쪽과 오른쪽에는 호환되는 형식이 있습니다.
SET @v1 = 'xyz'
SELECT @v1 = c1 FROM t1
IF 문 조건자조건자는 부울입니다.
IF (@v > 10)
WHILE 문 조건자조건자는 부울입니다.
WHILE (@v > 10)
INSERT 문값 및 열이 올바릅니다.
INSERT INTO t1(c1, c2) VALUES (99, 'xyz')
INSERT INTO t1 SELECT c1 FROM t2.
SELECT WHERE 조건자조건자는 부울입니다.
SELECT * FROM t1 WHERE c1 > 10
SELECT TOP 식식이 정수 또는 부동 소수점 형식입니다.
SELECT TOP 4 * FROM t1
SELECT TOP 1.5 PERCENT * FROM t1
UPDATE 문식 및 열에는 호환되는 형식이 있습니다.
UPDATE t1 SET c1 = 100
UPDATE 조건자조건자는 부울입니다.
UPDATE t1 SET c1 = 100
WHERE c1 > 100
UPDATE TOP 식식이 정수 또는 부동 소수점 형식입니다.
UPDATE TOP 4 table1
DELETE PREDICATE조건자는 부울입니다.
DELETE t1 WHERE c1 > 10
DELETE TOP 식식이 정수 또는 부동 소수점 형식입니다.
DELETE TOP 2 FROM t1
DECLARE 변수 선언초기 값 및 데이터 형식은 호환됩니다.
DECLARE @v INT = 10
EXECUTE 문 인수 및 반환 형식매개 변수 및 인수
CREATE PROCEDURE p1 (@p1 INT) AS
GO
EXECUTE p1 100
EXECUTE @v1 = p1 100
RETURN 문RETURN 식에 호환되는 데이터 형식이 있습니다.
CREATE FUNCTION f1() RETURNS INT
AS
BEGIN
  RETURN 100
END
MERGE 문 조건조건이 부울입니다.
MERGE t1 USING t2
ON t1.c1 = t2.c2
WHEN t1.c1 > 10 THEN DELETE

위반 문제를 해결하는 방법

데이터 형식을 일관되게 할당하고 필요한 위치에 형식을 명시적으로 변환하여 이러한 문제를 방지하고 해결할 수 있습니다. 데이터 형식을 명시적으로 변환하는 방법에 대한 자세한 내용은 Microsoft 웹 사이트 CAST 및 CONVERT(Transact-SQL)에서 이 페이지를 참조하세요.

예시

이 예제에서는 테이블에 데이터를 삽입하는 두 개의 저장 프로시저를 보여 줍니다. 첫 번째 프로시저인 procWithWarning은 데이터 형식을 암시적으로 변환합니다. procFixed의 두 번째 절차에서는 명시적 변환을 추가하여 성능을 최대화하고 모든 데이터를 유지하는 방법을 보여 줍니다.

CREATE TABLE [dbo].[Table2]
(
[ID] INT NOT NULL IDENTITY(0, 1),
[c1] INT NOT NULL,
[c2] INT NOT NULL,
[c3] BIGINT NOT NULL,
[Comment] VARCHAR (25)
)
ON [PRIMARY]

CREATE PROCEDURE [dbo].[procWithWarning]
(
@Value1 INT,
@Value2 INT,
@Value3 BIGINT,
@Comment CHAR(30)
)
AS
BEGIN
INSERT INTO [Table2] ([c1], [c2], [c3], Comment)
VALUES (@Value1, @Value2, @Value3, @Comment)

END

CREATE PROCEDURE [dbo].[procFixed]
(
@Value1 INT,
@Value2 INT,
@Value3 BIGINT,
@Comment CHAR(10)
)
AS
BEGIN
INSERT INTO [Table2] ([c1], [c2], [c3], Comment)
VALUES (@Value1, @Value2, @Value3, CAST(@Comment AS VARCHAR(25)))

END