I am creating an adapter for user data from a client. I cannot change their schema or change the values ββin my tables, although I can suggest new indexes. The approach is to use CTE to append and reformat user data to use our column names, enumerated values, etc. After the data is reformatted, our standard CTEs can be added and a query deduced from it, which can perform our standard analyzes.
Some of the values ββthat result from reformatting are NULL due to LEFT JOINs that do not match, or due to values ββin their data that are actually NULL.
My task is to replace the default values ββfor NULL in many fields, and also allow the inclusion of WHERE clauses in the query. Currently, ISNULL calls or CASE statements are used to handle default values. And now, by the time the WHERE clause expires, this substitution has already been completed, so that the end user with access to our query builder can filter the value, which can be the default value. If the filter value is the default value, select entries with NULL values ββthat have been replaced with the default value.
The problem is that if I have myField = ISNULL (myField, 'MyDefault') as my reformatting formula, and later there is WHERE myField = 'MyDefault' in the outer onion layer (later CTE), then this is where the sentence is not amenable to comparison: the query optimizer does not select my index on myField.
The partial solution that arises for me is to not do NULL replacements in my internal CTEs, and then have a CTE that introduces WHERE clauses, and then have an external CTE that does all the NULL replacements. Such a query may use indexes. (I checked this.) However, where clauses could no longer expect that checking the value against the default would also raise records with NULL values, since this substitution had not yet occurred.
Is there a way to do a null replacement, allow SARGABLE where the filters are, and filter the NULL values ββas if they were keeping the default value?
NOTE on the size of the problem. A typical example includes the CONNECTION of a 6 mm record table into a 7 mm record table with a many-to-many relationship that creates 12 million records. When the filter is SARGABLE, the query takes about 10 seconds. When it is not SARGABLE, it takes more than 10 minutes on one machine and more than three minutes on a faster machine.
DECISION COMMENT COMMENT:
The smart use of intersection, which allows you to compare a field with NULL or non-NULL without ISNULL or other functions that cannot be processed, can be used in our code with minimal changes in our previous requests.
COMMENT 2: Missing case
These are six cases:
- The selected value is not null and does not equal the default value and does not match the filter value. It should be excluded.
- The selected value is not null and not equal to the default value and corresponds to the filter value. Must include.
- The selected value is not null and equal to the default value and does not match the filter value. It should be excluded.
- The selected value is not null and equal to the default value and corresponds to the filter value. Must include.
- The selected value is null, and the filter value is not the default value. It should be excluded.
- The selected value is null and the default filter value. Must include.
Case 4 does not work using the proposed solution. The selected field is not null, so the first half of the intersection has a record with an anonymous value. But in the second half of the intersection, the NULLIF operator created a record with a null value. Intersection creates null records. Record rejected. I'm still looking for a solution that handles this case. So close...
Update solution:
I have a fix. Let's say that I smell [County name], and my default value is "Unknown" ...
where EXISTS ( select [County Name] intersect (select NULLIF('User selected county name', 'Unknown') union select 'User selected county name') )