Lorsque vous utilisez les opérateurs arithmétiques +, -, *, /, ou % pour exécuter conversion implicite ou explicite de valeurs constantes de type int, smallint, tinyint ou bigint vers les types de données float, real, decimal ou numeric, les règles que le serveur règles que SQL Server applique lorsqu'il calcule le type de données et la précision des de données et la précision des résultats de l'expression diffèrent selon que la requête est autoparamétrée ou non.
Par conséquent, des expressions similaires dans les requêtes peuvent parfois produire des résultats différents. Lorsqu'une requête n'est pas autoparamétrisée, la valeur de la constante est tout d'abord convertie en numérique, dont la précision est juste assez grande pour contenir la valeur de la constante, avant d'être convertie vers le type de données type de données spécifié. Par exemple, la valeur constante 1 est convertie en numeric (1, 0)
et la valeur constante 250 est convertie en numeric (3, 0)
.
Quand une requête est autoparamétrisée, la valeur constante est toujours convertie en numeric (10, 0)
avant de convertir en données finales final. Lorsque l'opérateur / est impliqué, non seulement la précision du type de résultat peut varier d'une requête à l'autre, mais la valeur du résultat peut également varier. du type de résultat peut varier entre des requêtes similaires, mais la valeur du résultat peut également varier. différer également. Par exemple, la valeur du résultat d'une requête autoparamétrisée qui inclut l'expression SELECT CAST (1.0 / 7 AS float)
sera différent de la valeur du résultat de la même requête qui n'est pas autoparamétrée, parce que les résultats de la requête autoparamétrée sont seront tronqués pour tenir dans le champ numeric (10, 0)
type de données.
Nota:
numeric (10, 0)
est équivalent à INT
.
Dans l'exemple ci-dessus, lorsque le dividende et le diviseur sont tous deux des nombres entiers, le type est traité comme suit INT
par exemple INT
/ INT
= INT
Si, d'un autre côté, l'un des types est forcé d'être un "vrai". NUMERIC
l'expression est traitée comme NUMERIC( 10, 0 )
/ NUMERIC( 10, 0 )
= NUMERIC( 21, 11 )
. Voir : Précision, échelle et longueur (Transact-SQL) pour une explication de la façon dont les types de résultats sont calculés.
Exemple :
EXEC sp_describe_first_result_set N'SELECT 1 as a, 7 as b, 1 / 7 AS Result'
EXEC sp_describe_first_result_set N'SELECT 1 as a, CONVERT( NUMERIC( 10, 0 ), 7 ) as b, CONVERT( INT, 1 ) / CONVERT( NUMERIC( 10, 0 ), 7 ) AS a'
Nota: NUMERIC
ne dispose que d'un nombre fixe de décimales (échelle) pour stocker les nombres fractionnaires. Cela devient important lorsque la division produit un résultat avec une partie décimale (infiniment) longue, par exemple 1 / 3, qui doit être tronquée pour s'adapter au type.
Cas du PO
La différence de résultats se résume à savoir si 12 est traité comme INT
/ NUMERIC( 10, 0 )
o NUMERIC( 2, 0 )
car cela affectera directement la précision (nombre de décimales) du résultat : decimal(19,16)
o decimal(11,8)
. J'ai enlevé CAST
y ROUND
pour montrer les types réels utilisés dans le calcul.
Paramètres d'entrée :
-- Note: on my machine "parameterization" option does not have any effect on below example
SELECT CONVERT( decimal (5, 3), 4.250 ) AS a, -- the type is explicitly defined in the table
0.01 AS b -- always becomes NUMERIC( 2, 2 )
12 AS c -- will either become NUMERIC( 2, 0 ) or NUMERIC( 10, 0 ) / INT
EXEC sp_describe_first_result_set N'SELECT CONVERT( decimal (5, 3), 4.250 ) AS a, 0.01 AS b, 12 AS c'
Dans le cas ci-dessus, il est traité comme INT
.
Vous pouvez le "forcer" à être traité en tant que NUMERIC( 2, 0 )
:
-- Note: on my machine "parameterization" option does not have any effect on below example
SELECT 0.01 AS b, ( 12 * 0.01 ) AS c
EXEC sp_describe_first_result_set N'SELECT ( 12 * 0.01 ) AS c'
-- Result: 0.12 numeric(5,2)
Formule de calcul du type de données produit : p1 + p2 + 1, s1 + s2
.
Pour trouver le type de départ, résolvez : 5 = x + 2 + 1, 2 = y + 2
pour obtenir 2, 0
c'est-à-dire NUMERIC( 2, 0 )
Le type de sortie du résultat sera le suivant :
-- 12 is NUMERIC( 10, 0 ) / INT
SELECT CONVERT( decimal (5, 3), 4.250 ) * CONVERT( decimal (2, 2), 0.01 ) / CONVERT( decimal(10, 0), 12 )
EXEC sp_describe_first_result_set N'SELECT CONVERT( decimal (5, 3), 4.250 ) * CONVERT( decimal (2, 2), 0.01 ) / CONVERT( decimal(10, 0), 12 )'
-- Result: 0.0035416666666666 decimal(19,16) -> rounding to 9 decimal places: 0.003541667
-- 12 is NUMERIC( 2, 0 )
SELECT CONVERT( decimal (5, 3), 4.250 ) * CONVERT( decimal (2, 2), 0.01 ) / CONVERT( decimal(2, 0), 12 )
EXEC sp_describe_first_result_set N'SELECT CONVERT( decimal (5, 3), 4.250 ) * CONVERT( decimal (2, 2), 0.01 ) / CONVERT( decimal(2, 0), 12 )'
-- Result: 0.00354166 decimal(11,8) -> rounding to 9 decimal places: 0.003541660
Pour voir comment les types de résultats sont calculés, voir Précision, échelle et longueur (Transact-SQL) .
Solution
Mettez vos littéraux et / ou vos résultats intermédiaires dans le type souhaité pour éviter les surprises, par exemple
SELECT CONVERT( decimal( 12, 7 ), CONVERT( decimal (5, 3), 4.250 ) * CONVERT( decimal (2, 2), 0.01 )) / CONVERT( decimal(2, 0), 12 )
EXEC sp_describe_first_result_set N'SELECT CONVERT( decimal( 12, 7 ), CONVERT( decimal (5, 3), 4.250 ) * CONVERT( decimal (2, 2), 0.01 )) / CONVERT( decimal(2, 0), 12 )'
-- Result: 0.0035416666 decimal(15,10) -> rounding to 9 decimal places: 0.003541660
Résumé :
Cette question est un cas complexe de : Division de 2 nombres à l'aide de la fonction CAST dans SQL server 2008R2 . La complexité provient du fait que SQL Server peut utiliser différents types de données dans différents scénarios.
Un mot sur le paramétrage simple
Je n'ai pu trouver qu'un seul article ( http://www.sqlteam.com ) sur le paramétrage simple qui mentionne quand/quand une requête doit être paramétrée automatiquement.
Nota: L'article date de 2007 et peut donc ne pas être d'actualité.
Le serveur SQL impose les restrictions suivantes sur les types de requêtes suivantes peuvent être paramétrés à l'aide de Simple Parameterization :
- Tableau unique - Pas de JOINs
- Pas de clause IN
- Non UNION
- Non SELECT INTO
- Pas de conseils pour les requêtes
- Pas de DISTINCT ou de TOP
- Pas de texte intégral, de serveurs liés ou de variables de table.
- Pas de sous-requêtes
- Non GROUP BY
- Pas de <> dans la clause WHERE
- Aucune fonction
- Pas de DELETE ou UPDATE avec clause FROM
- Les valeurs des paramètres ne peuvent pas affecter le plan
TechNet - Paramétrage simple L'article ne contient aucune information.
TechNet - Paramétrage forcé contient des informations, mais elles s'appliquent au paramétrage forcé.