I have the following schema/data in MSSQL Server 2019 (SQLFiddle):
CREATE TABLE products(
idn NUMERIC(9) PRIMARY KEY
);
CREATE TABLE sales(
idn NUMERIC(9) PRIMARY KEY,
pid VARCHAR(50) NOT NULL,
type VARCHAR(10) NOT NULL
);
INSERT INTO products (idn) VALUES (1);
INSERT INTO sales (idn, pid, type) VALUES (1, 1, 'number');
INSERT INTO sales (idn, pid, type) VALUES (2, 'Colgate', 'word');
sales
has mixed data i.e VARCHAR
and NUMERIC
. The transaction filter takes care of JOIN
ing correctly.
Why does the following SELECT
fail (For some reason beyond my control, the generated query has N
literals for strings.)?
SELECT
*
FROM
products
INNER JOIN sales ON products.idn = sales.pid
AND sales.type = N'number'
WHERE
products.idn in (1);
I don't see why casting NVARCHAR
to NUMERIC
is an issue:
SELECT CAST (N'1' as NUMERIC);
If I modify the query slightly it works:
SELECT
*
FROM
products
INNER JOIN sales ON products.idn = sales.pid
AND sales.type = N 'number'
WHERE
-- Selecting the same data from `sales`.
sales.pid in (1);
SELECT
*
FROM
products
INNER JOIN sales ON products.idn = sales.pid
-- Dropping the `N` prefix.
AND sales.type = 'number'
WHERE
products.idn in (1);
Best Answer
A conversion error will occur at run time when an attempt is made to convert the
sales.pid
value 'Colgate' value to numeric(9) to evaluate the join criteria. As to whether or not this actually happens depends on the order of evaluation in the execution plan.Below is the sales table clustered index scan predicate from the Unicode literal execution plan, showing the conversion occurs before the 'number' condition is evaluated:
The plan with the non-Unicode literal has the same shape except the predicate order in the scan operator is reversed:
Although the non-Unicode literal (or parameter) may workaround the problem, the query will still be vulnerable to run time errors. This can be addressed with
TRY_CAST
,TRY_CONVERT
,CASE
expression, etc. but it would be better to fix the data model such as to ensure only like or compatible data types are compared.