In SQL (Tsql) what's a good way to check mutually exclusive options are correct - sql

EDIT: damien the unbeliever, my apologies, trying to be terse I omitted saying that the design of the table is not under my control; this table is a "dump" of data we receive from another vendor, and I have to convert it from their format to ours. The reason I need a query is to find out if the data is consistent with assumptions in other parts of code. The solutions proposed looking for length or exact match of the concatenated strings are better than my pair of queries for the problem I described.
I have a working pair of queries for my problem, but I wondered if there's something a bit prettier. Exactly one of taxidflag1, taxidflag2, taxidflag3 should be filled in with * in each row. So I confirm they all have two blanks and one * like this. All fields are are non nullable.
select * from acct where 2 <>
(case when taxidFlag1 <> '' then 1 else 0 end) +
(case when taxidFlag2 <> '' then 1 else 0 end) +
(case when taxidFlag3 <> '' then 1 else 0 end)
select * from acct where 1 <>
(case when taxidFlag1 = '*' then 1 else 0 end) +
(case when taxidFlag2 = '*' then 1 else 0 end) +
(case when taxidFlag3 = '*' then 1 else 0 end)

You could do this:
select * from acct where taxidFlag1 + taxidFlag2 + taxidFlag3 = '*';
This condition is only true if two are empty ('') and one is a asterisk (*).

select *
from
acct a1
where
(select count(*) from acct unpivot (foo for taxidFlag in (taxidFlag1, taxidFlag2, taxidFlag3)) as unp where unp.row_id = a1.row_id and foo = '*') <> 1
;
where row_id is your primary key field.

Related

Why Would Unknown Column Be Referenced in SQL Query

I am in the process of updating some SQL queries to run against MariaDB instead of via SQL Anywhere. One query I'm running is erroring with this:
Error Code: 1054. Unknown column 'choice' in 'field list'
That is for this query:
SELECT
(select firstname||' '||lastname||' ('||service||')' from staff_members where id_number = customer_assignment_reviews.staff_member_id) as Rep,
(select customer_firstname||' '|| customer_lastname from customers where id_number = customer_assignment_reviews.cs_id) as Cus,
last_modified as "Response Date",replace(review_reason,'’','') as "Reason",
(Select choice = CASE
when accepted = 0 then 'No'
when accepted = 1 then 'Yes'
end) as "Accepted?"
FROM customer_assignment_reviews
where staff_member_id in (Select id_number from kar.staff_members where division_id = 6)
and "Response Date" between today() - 7 and today() /* Date Range */
and "Accepted?" = 'No'
Order by 3 desc
Is this error message as straightforward as it sounds? It's simply saying the column "choice" doesn't exist on the target table?
I'm just trying to reason through why this code (which I inherited) would be referencing a column that does not exist. Could something be expected here at runtime?
You don't need to use subquery in SELECT list
SELECT
-- ...
(Select choice = CASE
when accepted = 0 then 'No'
when accepted = 1 then 'Yes'
end) as "Accepted?"
=>
SELECT
CASE
when accepted = 0 then 'No'
when accepted = 1 then 'Yes'
end as "Accepted?"
Additionaly syntax SELECT alias = expression is only T-SQL specific:
SELECT alias = 1
<=>
SELECT 1 AS alias
What is this supposed to mean?
(Select choice = CASE
when accepted = 0 then 'No'
when accepted = 1 then 'Yes'
end) as "Accepted?"
Very importantly, a select is not needed here. You might mean:
(case when accepted = 0 then 'No'
when accepted = 1 then 'Yes'
end) as is_accepted -- prefer to not have to need escape characters
If accepted only takes those two values, you can simplify this to:
elt(accepted + 1, 'No', 'Yes') as is_accepted

SQL : finding which clause is making my query returning no answer

My query is basic and look like this :
SELECT ID FROM Table WHERE CRIT1='a' AND CRIT2='b' AND CRIT3='c'
However it sometimes return no value. This is normal because there is no match in the table.
To help my users to find which criteria is too restrictive, I would like to find another query which tell me if it is because of clause CRIT1, CRIT2 or CRIT3 that I have no answer.
Currently, I've done it this way (using pseudo code) :
If ( SELECT ID FROM Table WHERE CRIT1='a' returns EOF )
Then WrongCriteria="CRIT1"
Elseif ( SELECT ID FROM Table WHERE CRIT1='a' AND CRIT2='b' returns EOF )
Then WrongCriteria="CRIT2"
Elseif ( SELECT ID FROM Table WHERE CRIT1='a' AND CRIT2='b' AND CRIT3='c' returns EOF )
Then WrongCriteria="CRIT3"
It works ... but there are several queries and each of them is very slow due to the poor network response time.
My question is thus : It is possible to do the above pseudo-code in one single SQL query?
You can combine three queries into one by using SUM on a conditional:
SELECT
SUM(CASE WHEN CRIT1='a' THEN 1 ELSE 0 END) as CRIT1
, SUM(CASE WHEN CRIT1='a' AND CRIT2='b' THEN 1 ELSE 0 END) as CRIT2
, SUM(CASE WHEN CRIT1='a' AND CRIT2='b' AND CRIT3='c' THEN 1 ELSE 0 END) as CRIT3
FROM MyTable
Zero in a column corresponds to the criterion being to restrictive.
Note that this is only a different implementation of your three queries, which "prioritizes" the criteria in a specific way (crit1 then crit2 then crit3). In theory, with three criteria you want to test all individual ones, plus three combinations of pairs, i.e get six counts for these conditions:
CRIT1='a'
CRIT2='b'
CRIT3='c'
CRIT1='a' && CRIT2='b'
CRIT1='a' && CRIT3='c'
CRIT2='b' && CRIT3='c'
The above six counts would give you a full picture of which criteria are too restrictive.
Yes it's possible to do this check in a single query using 'OR' operator.
I'm assuming it's only one condition which can be wrong at a time:
SELECT CASE WHEN CRIT1 <> 'a' THEN 'CRIT1'
WHEN CRIT2 <> 'b' THEN 'CRIT2'
WHEN CRIT3 <> 'c' THEN 'CRIT3' END AS WrongCriteria
FROM Table WHERE CRIT1<>'a' OR CRIT2<>'b' OR CRIT3<>'c'
To show all combinations of restrictions:
SELECT
COALESCE( 'Conditions:'
+ NULLIF(
( CASE WHEN CRIT1 <> 'a' THEN ' CRIT1' ELSE '' END )
+ ( CASE WHEN CRIT2 <> 'b' THEN ' CRIT2' ELSE '' END )
+ ( CASE WHEN CRIT3 <> 'c' THEN ' CRIT3' ELSE '' END ),
'' ),
'None' ) AS Restrictions
FROM MyTable

Sum on multiple columns with nullable values

I have to edit a stored procedure who has to return the sums of three columns having nullable values. If there is a null value, I need to cast it to 0
Here is a screenshot of data :
And here is the originial request using the first column only :
SELECT SUM(reglProj.Montant) /* SUM of 'Montant', 'FraisMagasing', 'FraisVendeur' instead */ AS SommeReglement
FROM Projet.LigneEcheancierProjet ligne
INNER JOIN Projet.ReglementProjetEcheance reglProj ON reglProj.LigneEcheancierProjetId = ligne.LigneEcheancierProjetId
....
Do you have some best practices using the sum and case conditions in T-SQL ?
--ANSI standard
SELECT SUM(COALESCE(col1,0)) + SUM(COALESCE(col2,0)) + SUM(COALESCE(col3,0))
--SQL Server Style
SELECT SUM(ISNULL(col1,0)) + SUM(ISNULL(col2,0)) + SUM(ISNULL(col3,0))
--The one wthout functions. It will work the same as previous OR FASTER.
SELECT SUM(CASE WHEN col1 IS NULL THEN 0 ELSE col1 END) + SUM(CASE WHEN col2 IS NULL THEN 0 ELSE col2 END) + SUM(CASE WHEN col3 IS NULL THEN 0 ELSE col3 END)
Choose one for yourself.
OR you might need following (if you want to add sums by row):
--ANSI standard
SELECT SUM(COALESCE(col1,0) +COALESCE(col2,0) + COALESCE(col3,0))
--SQL Server Style
SELECT SUM(ISNULL(col1,0)+ ISNULL(col2,0) + ISNULL(col3,0))
--The one wthout functions. It will work the same as previous OR FASTER.
SELECT SUM(CASE WHEN col1 IS NULL THEN 0 ELSE col1 END + CASE WHEN col2 IS NULL THEN 0 ELSE col2 END + CASE WHEN col3 IS NULL THEN 0 ELSE col3 END)
In Sql Server, (and probably in most if not all relational databases) the SUM Aggregation function ignores null values by default, so there really is no need to use coalesce or isnull inside it.
If you want the sum of all 3 columns for every single row, then you need to use isnull:
SELECT ISNULL(reglProj.Montant,0) +
ISNULL(reglProj.FraisMagasing ,0) +
ISNULL(reglProj.FraisVendeur,0)
FROM Projet.LigneEcheancierProjet ligne
INNER JOIN Projet.ReglementProjetEcheance reglProj
ON reglProj.LigneEcheancierProjetId = ligne.LigneEcheancierProjetId
If you need the aggregated sum of all 3 columns you can simply do it like this:
SELECT ISNULL(SUM(reglProj.Montant), 0) +
ISNULL(SUM(reglProj.FraisMagasing), 0) +
ISNULL(SUM(reglProj.FraisVendeur), 0)
FROM Projet.LigneEcheancierProjet ligne
INNER JOIN Projet.ReglementProjetEcheance reglProj
ON reglProj.LigneEcheancierProjetId = ligne.LigneEcheancierProjetId
It seems you are looking for ISNULL actually
SELECT SUM( ISNULL(reglProj.Montant,0) + ISNULL(FraisMagasing,0)+ ISNULL(FraisVendeur,0)) AS SommeReglement
FROM Projet.LigneEcheancierProjet ligne
INNER JOIN Projet.ReglementProjetEcheance reglProj ON reglProj.LigneEcheancierProjetId = ligne.LigneEcheancierProjetId

Display columns that contain a carriage return

I have a Students table, which contains 7 address fields.
I need to display 1 row each for student where the address fields have carriage return, if any.
It's confused after this.
The 9th column (1st column - Student ID, 2-8 column - 7 address fields) must contain the list of column names which have a carriage return ( like addr_1, addr_3, 1 for each student ID separated by a comma)
The 10th column must contain the illegal character (in this case, carriage return).
This code must be further extended to other illegal characters identified now and then and a report has to be generated.
I am unable to work on 9th and 10th columns. Can anyone help?
SELECT pty.id,
a.addr_1,
a.addr_2,
a.addr_3,
a.addr_4,
a.addr_5,
a.addr_6,
a.addr_7
FROM addr a
inner join contact cON a.idf = c.add_idf
inner join pty ON c.pty_id = pty.id
WHERE
INSTR(a.addr_1,CHR(13)) > 0 OR
INSTR(a.addr_2,CHR(13)) > 0 OR
INSTR(a.addr_3,CHR(13)) > 0 OR
INSTR(a.addr_4,CHR(13)) > 0 OR
INSTR(a.addr_5,CHR(13)) > 0 OR
INSTR(a.addr_6,CHR(13)) > 0 OR
INSTR(a.addr_7,CHR(13)) > 0;
This sounds like a homework question. So, let me give you some hints:
(1) You can generate a table using syntax, such as:
select chr(13) as badchar from dual union all
select '!' . . .
(2) You can cross join this into the table and use a very similar where clause.
(3) You can then select the bad character from the table.
(4) You'll need an aggregation.
Actually, I would be inclined to drop the requirement of one row per student and instead have one row per student/bad character. Here is an approach:
select a.id,
a.addr_1, a.addr_2, a.addr_3, a.addr_4, a.addr_5, a.addr_6, a.addr_7,
((case when INSTR(a.addr_1, b.badChar) > 0 then 'addr_1,' else '' end) ||
(case when INSTR(a.addr_2, b.badChar) > 0 then 'addr_2,' else '' end) ||
(case when INSTR(a.addr_3, b.badChar) > 0 then 'addr_3,' else '' end) ||
(case when INSTR(a.addr_4, b.badChar) > 0 then 'addr_4,' else '' end) ||
(case when INSTR(a.addr_5, b.badChar) > 0 then 'addr_5,' else '' end) ||
(case when INSTR(a.addr_6, b.badChar) > 0 then 'addr_6,' else '' end) ||
(case when INSTR(a.addr_7, b.badChar) > 0 then 'addr_7,' else '' end)
) as addrs,
b.badChar
from a cross join
(select chr(13) as badChar from dual) as b
WHERE INSTR(a.addr_1, b.badChar) > 0 OR
INSTR(a.addr_2, b.badChar) > 0 OR
INSTR(a.addr_3, b.badChar) > 0 OR
INSTR(a.addr_4, b.badChar) > 0 OR
INSTR(a.addr_5, b.badChar) > 0 OR
INSTR(a.addr_6, b.badChar) > 0 OR
INSTR(a.addr_7, b.badChar) > 0;
It leaves an extra comma at the end of the column names. This can be removed by making this a subquery and doing string manipulations at the next level.
To put all badchars on one line would require an aggregation. However, I am not clear what the 9th and 10th columns would contain in that case.
9th column would be with a case when instr(...) then 1 else 0 end || case when instr(...) then
create table tmp (vc varchar2(20), vc2 varchar2(20));
insert into tmp values ('abcd','bcda');
insert into tmp values ('bcd','bcda');
select
case when instr(vc,'a')>0 then 'col1' else null end ||
case when instr(vc2,'a')>0 then 'col2' else null end
from tmp;
As for the second problem, you can just put 'RETURN' in the 10th column. Since you are looking for only one forbidden character and get only lines which have it.
When you come up with a solution dealing with several forbidden chars, I'll update.
In similar situations I've gone for a Big Hammer and just detected non-printing control codes with a REGEXP_LIKE(col1,'[:cntrl:]'), because next someone will add a tab or something else that breaks the data.
Is it too much to ask that a check constraint be placed on the columns to prevent this from happening?

Check any 4 out 10 conditions are satisfied in SQL

I apologize for asking a very vague question but here it is.
I have to write a SQL query in SQL Server as follows.
I have a table say tblA having 10 columns from col1, col2,.....col10.
Each column is not null and definitely holds some value and all of type int.
The query should be to select all such records in which at least 4 columns are matching with given filter criteria, where the filter criteria has values for all 10 columns.
I googled dint get a clue. It needs to be done in SQL server and single query.
Please suggest.
Thanks in advance.
SELECT *
FROM
yourtable
WHERE
case col1 when #value1 then 1 else 0 end +
case col2 when #value2 then 1 else 0 end +
...
case col10 when #value10 then 1 else 0 end
>=4
You can use CASE expressions to determine if four or more columns match:
SELECT *
FROM YourTable
WHERE CASE WHEN Col1 = Filter1 THEN 1 ELSE 0 END +
CASE WHEN Col2 = Filter2 THEN 1 ELSE 0 END +
....
CASE WHEN Col10 = Filter10 THEN 1 ELSE 0 END >= 4
You can do something like this:
select *
from (select t.*,
(case when col1 <whatever> then 1 else 0 end) as col01_matches,
(case when col2 <whatever> then 1 else 0 end) as col02_matches,
. . .
from t
) t
where (col1_matches + col2_matches + col3_matches . . .) >= 4
This creates a separate indicator variable for each match. You could also do the sum in the subquery, in a single variable. I would prefer to have each match separately, just in case the logic gets more complicated or I want to see what matches.