Megosztás a következőn keresztül:


T-SQL tervezési problémái

A következőkre vonatkozik:SQL ServerAzure SQL DatabaseFelügyelt Azure SQL-példánySQL-adatbázis a Microsoft Fabricben

Ha az adatbázisprojektben elemzi a T-SQL-kódot, egy vagy több figyelmeztetés tervezési problémaként kategorizálható. A következő helyzetek elkerülése érdekében a tervezéssel kapcsolatos problémákat kell elhárítania:

  • Az adatbázis későbbi módosításai megszakíthatják az attól függő alkalmazásokat.
  • Előfordulhat, hogy a kód nem a várt eredményt hozza létre.
  • A kód megszakadhat, ha az SQL Server jövőbeli kiadásaival futtatja.

Általában nem szabad letiltania egy tervezési problémát, mert az akár most, akár a jövőben megtörheti az alkalmazást.

A megadott szabályok a következő tervezési problémákat azonosítják:

SR0001: A SELECT * elkerülése tárolt eljárásokban, nézetekben és táblaértékű függvényekben

Ha helyettesítő karaktert használ egy tárolt eljárás, nézet vagy tábla értékű függvényben egy tábla vagy nézet összes oszlopának kijelöléséhez, a visszaadott oszlopok száma vagy alakja megváltozhat, ha az alapul szolgáló tábla vagy nézet megváltozik. Az oszlop alakja a típus és a méret kombinációja. Ez a variancia problémákat okozhat a tárolt eljárást, nézetet vagy táblaértékű függvényt használó alkalmazásokban, mert ezek a felhasználók eltérő számú oszlopra számíthatnak.

Szabálysértések kijavítása

A tárolt eljárás, a nézet vagy a táblaértékű függvény felhasználóit megvédheti a sémamódosításoktól, ha a helyettesítő karaktert egy teljes oszlopnévlistára cseréli.

Example

Az alábbi példa először egy [Table2] nevű táblát határoz meg, majd két tárolt eljárást határoz meg. Az első eljárás tartalmaz egy SELECT *, amely megsérti az SR0001 szabályt. A második eljárás elkerüli SELECT *, és explicit módon felsorolja a SELECT utasítás oszlopait.

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: Fontolja meg a SCOPE_IDENTITY használatát @@IDENTITY helyett

Mivel @@IDENTITY globális identitásérték, előfordulhat, hogy az aktuális hatókörön kívül lett frissítve, és váratlan értéket kapott. A triggerek, beleértve a replikációban használt beágyazott triggerek, képesek frissíteni az @@IDENTITY értéket a jelenlegi hatókörön kívül.

Szabálysértések kijavítása

A probléma megoldásához a @@IDENTITY hivatkozásait SCOPE_IDENTITY kell cserélnie, amely a felhasználói utasítás hatókörében a legújabb identitásértéket adja vissza.

Example

Az első példában @@IDENTITY egy tárolt eljárásban használatos, amely adatokat szúr be egy táblába. Ezt követően a rendszer közzéteszi a táblát az egyesítési replikációhoz, amely triggereket ad hozzá a közzétett táblákhoz. Ezért az @@IDENTITY vissza tudja adni a beszúrási művelet során keletkezett értéket egy replikációs rendszertábla helyett a felhasználói táblába.

A Sales.Customer tábla maximális identitásértéke 29483. Ha beszúr egy sort a táblázatba, @@IDENTITY és SCOPE_IDENTITY() különböző értékeket ad vissza. SCOPE_IDENTITY() a beszúrási művelet értékét adja vissza a felhasználói táblába, de @@IDENTITY a beszúrási művelet értékét adja vissza a replikációs rendszer táblájába.

A második példa bemutatja, hogyan férhet hozzá a beszúrt identitásértékhez a SCOPE_IDENTITY() használatával, és hogyan oldhatja fel a figyelmeztetést.

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: Kerülje az 1 vagy 2 méretű változóhossz-típusok használatát

Ha változó hosszúságú adattípusokat (például VARCHAR, NVARCHAR és VARBINARY) használ, további tárolási költséggel jár az adattípusban tárolt érték hosszának nyomon követése. Emellett a változó hosszúságú oszlopokat a rendszer az összes rögzített hosszúságú oszlop után tárolja, ami hatással lehet a teljesítményre. Figyelmeztetést is kap, ha változóhosszt deklarál, például VARCHAR értéket, de nem ad meg hosszt. Ez a figyelmeztetés azért fordul elő, mert ha nincs meghatározva, az alapértelmezett hossz 1.

Szabálysértések kijavítása

Ha a típus hossza nagyon kicsi (1-es vagy 2-es méret), és konzisztens, deklarálja őket rögzített hosszúságú típusként, például CHAR, NCHAR és BINÁRIS értékként.

Example

Ez a példa két tábla definícióit mutatja be. Az első tábla egy változó hosszúságú karakterláncot hossza 2-re deklarál. A második táblázat ehelyett rögzített hosszúságú sztringet deklarál, ami elkerüli a figyelmeztetést.

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]

A változóhosszúság-típusok adatait a rendszer fizikailag tárolja a rögzített hosszúságú adatok után. Ezért adatáthelyezést fog okozni, ha egy oszlopot változóról rögzített hosszra módosít egy olyan táblában, amely nem üres.

SR0010: Ne használjon elavult szintaxist táblák vagy nézetek összekapcsolásakor

Az elavult szintaxist használó illesztések két kategóriába sorolhatók:

  • Belső illesztés: Belső illesztés esetén az összekapcsolt oszlopok értékeit egy összehasonlító operátor (például =, <= >stb.) használatával hasonlítjuk össze. A belső illesztések csak akkor adnak vissza sorokat, ha az egyes táblákból legalább egy sor megfelel az illesztés feltételének.
  • Külső illesztés: A külső illesztések a FROM záradékban megadott táblák vagy nézetek legalább egyikének összes sorát visszaadják, amennyiben ezek a sorok megfelelnek a WHERE vagy a HAVING keresési feltételnek. Ha = vagy = külső illesztést ad meg, elavult szintaxist használ.

Szabálysértések kijavítása

A belső illesztés szabálysértésének kijavításához használja a szintaxist INNER JOIN .

Ha egy külső illesztés szabálysértését szeretné kijavítani, használja a megfelelő OUTER JOIN szintaxist. A következő lehetőségek közül választhat:

  • BAL OLDALI KÜLSŐ ILLESZTÉS VAGY BAL OLDALI ILLESZTÉS
  • JOBB KÜLSŐ ILLESZTÉS VAGY JOBB ILLESZTÉS

Az elavult szintaxisra és a frissített szintaxisra az alábbi példák nyújtanak példákat. Az illesztésekről további információt a Csatlakozások oldalon talál.

Examples

A hat példa a következő lehetőségeket mutatja be:

  1. Az 1. példa egy belső illesztés elavult szintaxisát mutatja be.
  2. A 2. példa bemutatja, hogyan frissítheti az 1. példát az aktuális szintaxis használatára.
  3. A 3. példa egy bal oldali külső illesztés elavult szintaxisát mutatja be.
  4. A 4. példa bemutatja, hogyan frissíthető a 2. példa az aktuális szintaxis használatára.
  5. Az 5. példa egy jobb oldali külső illesztés elavult szintaxisát mutatja be.
  6. A 6. példa bemutatja, hogyan frissítheti az 5. példát az aktuális szintaxis használatára.
-- 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: A kimeneti paraméter (paraméter) nem minden kódútvonalban van kitöltve

Ez a szabály azonosítja azokat a kódot, amelyekben a kimeneti paraméter nem egy vagy több kódútvonal értékére van beállítva a tárolt eljárással vagy függvénnyel. Ez a szabály nem határozza meg, hogy a kimeneti paraméter mely elérési utakon legyen beállítva. Ha több kimeneti paraméternél is jelentkezik ez a probléma, minden paraméterhez egy figyelmeztetés jelenik meg.

Szabálysértések kijavítása

Ezt a problémát kétféleképpen lehet kijavítani. Ezt a problémát a legegyszerűbben akkor háríthatja el, ha a kimeneti paramétereket az eljárás törzsének elején egy alapértelmezett értékre inicializálja. Másik lehetőségként beállíthatja a kimeneti paramétert olyan értékre is az adott kódútvonalakban, amelyekben a paraméter nincs beállítva. Egy összetett eljárás során azonban figyelmen kívül hagyhat egy nem gyakori kódútvonalat.

Important

Ha megad egy értéket az eljárásdeklarációban, például CREATE PROC MyProcedure (@param1 INT = 10 OUTPUT) nem oldja meg a problémát. Az eljárás törzsén belül értéket kell hozzárendelnie a kimeneti paraméterhez.

Example

Az alábbi példa két egyszerű eljárást mutat be. Az első eljárás nem állítja be a kimeneti paraméter értékét. @Sum A második eljárás inicializálja a @Sum paramétert az eljárás elején, ami biztosítja, hogy az érték minden kódútvonalon be legyen állítva.

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: Adatvesztés fordulhat elő a(z) {Type1} és {Type2} közötti öntéskor

Ha az adattípusok nincsenek inkonzisztensen oszlopokhoz, változókhoz vagy paraméterekhez rendelve, a rendszer implicit módon konvertálja őket az objektumokat tartalmazó Transact-SQL kód futtatásakor. Az ilyen típusú átalakítás nem csak csökkenti a teljesítményt, hanem bizonyos esetekben az adatok finom elvesztését is okozza. Előfordulhat például, hogy egy táblavizsgálat akkor fut, ha egy WHERE záradék minden oszlopát konvertálni kell. Még rosszabb, hogy az adatok elveszhetnek, ha egy Unicode-sztring más kódlapot használó ASCII-sztringgé alakul át.

Ez a szabály NEM:

  • Ellenőrizze a számított oszlop típusát, mert a típus futási időig nem ismert.
  • Elemezzen bármit a CASE utasításon belül. Nem elemzi a CASE utasítás visszatérési értékét sem.
  • Az ISNULL-hívás bemeneti paramétereinek vagy visszatérési értékének elemzése

Ez a táblázat az SR0014 szabály által érintett ellenőrzéseket foglalja össze:

Nyelvi szerkezetMi van ellenőrizve?Example
Paraméterek alapértelmezett értékeParaméter adattípusa
CREATE PROCEDURE p1(@p1 INT = 1)
AS
BEGIN
END
INDEX-predikátum LÉTREHOZÁSAA predikátum logikai típusú
CREATE INDEX index1 ON table1 (column1)
WHERE column1 > 10
A LEFT vagy a RIGHT függvény argumentumaiKarakterlánc argumentumtípusa és hossza
SET @v = LEFT('abc', 2)
A CAST és a CONVERT függvény argumentumaiA kifejezés és a típusok érvényesek
SET @v = CAST('abc' AS CHAR(10))
SET utasításA bal és a jobb oldalon kompatibilis típusok vannak
SET @v1 = 'xyz'
SELECT @v1 = c1 FROM t1
HA utasítás predikátumA predikátum logikai típusú
IF (@v > 10)
WHILE utasítás predikátumA predikátum logikai típusú
WHILE (@v > 10)
INSERT utasításAz értékek és oszlopok helyesek
INSERT INTO t1(c1, c2) VALUES (99, 'xyz')
INSERT INTO t1 SELECT c1 FROM t2.
WHERE predikátum kiválasztásaA predikátum logikai típusú
SELECT * FROM t1 WHERE c1 > 10
SELECT TOP kifejezésA kifejezés egész szám vagy lebegőpontos típus
SELECT TOP 4 * FROM t1
SELECT TOP 1.5 PERCENT * FROM t1
UPDATE utasításA kifejezés és az oszlop kompatibilis típusokkal rendelkezik
UPDATE t1 SET c1 = 100
UPDATE predikátumA predikátum logikai típusú
UPDATE t1 SET c1 = 100
WHERE c1 > 100
TOP-kifejezés FRISSÍTÉSEA kifejezés egész szám vagy lebegőpontos típus
UPDATE TOP 4 table1
PREDIKÁTUM TÖRLÉSEA predikátum logikai típusú
DELETE t1 WHERE c1 > 10
DELETE TOP kifejezésA kifejezés egész szám vagy lebegőpontos típus
DELETE TOP 2 FROM t1
Változó deklarálásaA kezdeti érték és az adattípus kompatibilis
DECLARE @v INT = 10
EXECUTE utasítás argumentumai és visszatérési típusaParaméterek és argumentumok
CREATE PROCEDURE p1 (@p1 INT) AS
GO
EXECUTE p1 100
EXECUTE @v1 = p1 100
RETURN utasításA RETURN kifejezés kompatibilis adattípussal rendelkezik
CREATE FUNCTION f1() RETURNS INT
AS
BEGIN
  RETURN 100
END
MERGE utasítás feltételeiA feltétel logikai
MERGE t1 USING t2
ON t1.c1 = t2.c2
WHEN t1.c1 > 10 THEN DELETE

Szabálysértések kijavítása

Ezeket a problémákat elkerülheti és megoldhatja, ha következetesen rendel hozzá adattípusokat, és explicit módon konvertálja azokat a típusokat, ahol szükség van rájuk. Az adattípusok explicit konvertálásával kapcsolatos további információkért tekintse meg ezt a lapot a Microsoft webhelyén: CAST és CONVERT (Transact-SQL).

Example

Ez a példa két tárolt eljárást mutat be, amelyek adatokat szúrnak be egy táblába. Az első eljárás, a procWithWarning egy adattípus implicit átalakítását eredményezi. A második procFixed eljárás bemutatja, hogyan adhat hozzá explicit átalakítást a teljesítmény maximalizálása és az összes adat megőrzése érdekében.

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