How to fix a query which mixes AND and OR conditions? - sql

I need to query a table with a filtering condition on three columns:
select *
from table a
where a.order = car
and a.color !=red
or a.automatic !=auto
This just checks the first conditions and ignores the second one.
I just can't get my head around it :(

Its not clear from your question but all you probably need is to wrap parens around your OR condition
Given this following table and rows
CREATE TABLE table_a
(
id int primary key,
a_order varchar2(20),
color varchar2(40),
automatic varchar2(10)
)
/
INSERT INTO table_a VALUES (1,'car','red','auto') /
INSERT INTO table_a VALUES (2,'car','red','manual') /
INSERT INTO table_a VALUES (3,'car','blue','auto') /
INSERT INTO table_a VALUES (4,'car','blue','manual') /
INSERT INTO table_a VALUES (5,'truck','red','auto') /
INSERT INTO table_a VALUES (6,'truck','red','manual') /
INSERT INTO table_a VALUES (7,'truck','blue','auto') /
INSERT INTO table_a VALUES (8,'truck','blue','manual') /
This select statement will return records 2, 3 and 4 since they are all cars and are either blue or manual
SELECT *
FROM table_a a
WHERE a.a_order = 'car'
AND ( a.color != 'red'
OR a.automatic != 'auto' )
Demo
Another option is to do the following using De Morgan's law "The negation of a disjunction is the conjunction of the negations."
SELECT *
FROM table_a a
WHERE a.a_order = 'car'
AND NOT ( a.color = 'red'
AND a.automatic = 'auto' )
Demo

The priority of AND is higher than OR logical operator. You can use OR condition within parenthesis.
SELECT *
FROM table_a a
WHERE a.a_order = 'car'
AND NOT ( a.color = 'red'
AND a.automatic = 'auto' )

Related

Get different query back based on a condition in PostgreSQL

I'm having a hard time with sql and probably this will look stupid but it shows what I am trying to achieve.
SELECT
CASE WHEN ( ( SELECT 1 FROM table_1 WHERE = condition ) IS NULL ) THEN
SELECT 'No result'::varchar
ELSE
SELECT
val_1,
val_2,
val_3
FROM
table_1
END;
A null answer is not good in my situation, I can't just use the sub-query as the main. And even if I could that would still leave the question open if the two tables were NOT the same like:
SELECT
CASE WHEN ( ( SELECT 1 FROM table_1 WHERE = condition ) IS NULL ) THEN
SELECT 'No result'::varchar
ELSE
SELECT
val_1,
val_2,
val_3
FROM
table_2 --TABLE REPLACED!
END;
As CASE-WHEN only works for one column it would be horrifying to have 20 of them with the same condition. Any help is appreciated! Thanks!
So you want to SELECT the table_log and if the result is not NULL show it to the client and if it is NULL show a message?
I created a fake table for testing. What you are looking for is the last SELECT-statement:
DROP TABLE IF EXISTS table_log;
CREATE TEMP TABLE table_log (
id INTEGER
,log_info VARCHAR)
;
INSERT INTO table_log VALUES
(1, 'test_entry')
;
ANALYZE table_log;
SELECT
COALESCE(b.log_info, 'No changes done!') AS log_info
FROM
(SELECT 'Fake-Data') a
LEFT OUTER JOIN (SELECT * FROM table_log WHERE id = 1) b ON (1=1);
If the given id = 1, you get the result, if it is something else (because it is not in the test-table) the premade message is given.
Here is a link to the db<>fiddle.

How do i create a DB2 UNION query using variables from a list

So i have a union query like:
select count(id)
from table 1
where membernumber = 'x'
and castnumber = 'y'
union
select count(id)
from table 1
where membernumber = 'x'
and castnumber = 'y'
union
etc...
There will be over 200 unions coming from a list 2x 200 table with values for x and y in each row. So each union query has to get the value of x and y from the corresponding row (not in any particular order).
How can i achieve that ?
Thanks
Try this:
DECLARE GLOBAL TEMPORARY TABLE
SESSION.PARAMETERS
(
MEMBERNUMBER INT
, CASTNUMBER INT
) DEFINITION ONLY WITH REPLACE
ON COMMIT PRESERVE ROWS NOT LOGGED;
-- Insert all the the constants in your application with
INSERT INTO SESSION.PARAMETERS
(MEMBERNUMBER, CASTNUMBER)
VALUES (?, ?);
-- I don't know the meaning of the result you want to get
-- but it's equivalent
select distinct count(t.id)
from table1 t
join session.parameters p
on p.membernumber = t.membernumber
and p.castnumber = t.castnumber
group by t.membernumber, t.castnumber;

Update column based on IF Else Condition

I have two tables A and B
Table A
ID_number as PK
first_name,
L_Name
Table B
ID_number,
Email_id,
Flag
I have several people who have multiple email ID and are already flagged as X on table B.
Whereas i am trying to find list of people who have an email id or multiple email ID, but were never flagged.
e.g John clark might have 2 email in table B, but was never flagged.
Simply use not exists:
select a.*
from a
where not exists (select 1
from b
where b.id_number = a.id_number and b.flag = 'X'
);
You may want to perform an update, but your question seems to be only about selecting (probably to update based on select). It should be something like this:
SELECT A.L_Name
FROM A
WHERE NOT EXISTS (
SELECT 1
FROM B
WHERE B.ID_number = A.ID_number AND B.Flag = 'X'
)
OR the LEFT JOIN version
SELECT 1
FROM A
LEFT JOIN B ON B.ID_number = A.ID_number AND B.Flag = 'X'
WHER B.ID_number IS NULL
Usually, the first version is faster than the second one.
Forget Table A...
SELECT DISTINCT ID_number FROM table_b t1
WHERE NOT EXISTS(
SELECT NULL FROM table_b t2 WHERE t1.ID_number=t2.ID_number AND t2.flag='X'
)
Judging by your responses in the comments, I believe this is what you are looking for:
--drop table update_test;
create table update_test
(
id_num number,
email_id number,
flag varchar2(1) default null
);
insert into update_test values (1, 1, null);
insert into update_test values (1, 2, null);
insert into update_test values (2, 3, null);
insert into update_test values (2, 7, null);
insert into update_test values (3, 2, null);
insert into update_test values (3, 3, 'X');
insert into update_test values (3, 7, null);
select * from update_test;
select id_num, min(email_id)
from update_test
group by id_num;
update update_test ut1
set flag = case
when email_id = (
select min(email_id)
from update_test ut2
where ut2.id_num = ut1.id_num
) then 'X'
else null end
where id_num not in (
select id_num
from update_test
where Flag is not null);
The last update statement will update and set the Flag field on the record for each id_num group with the lowest email_id. If the id_num group already has the Flag field set for one it will ignore it.

SQL Querying on tuple values

I need to write a write a SQL query that selects values from a table based on several tuples of selection criteria. It could be done using a where clause like this :
where (a = 1 and b='a') or (a=5 and b='s')
Is the best way to select:
select a, pk from x where a in (1,5)
select b, pk from x where b in ('a','s')
and join the result of the two queries using the primary key?
do you mean something(a self join) like this:
select x.a, x.pk
from x
join x x2 on x.pk=x2.pk
where x.a in (1,5)
and x2.b in ('a','s')
?
You can use join on table expression from VALUES. You can add in VALUES as much rows as you want. It will work on MSSQL:
DECLARE #x TABLE ( a INT, b CHAR(1) )
INSERT INTO #x
VALUES ( 1, 'a' ),
( 1, 'b' ),
( 1, 'c' ),
( 2, 'd' ),
( 2, 'e' ),
( 5, 'f' ),
( 5, 's' )
SELECT x.*
FROM #x x
JOIN (
VALUES ( 1, 'a'),
( 5, 's')
) AS v( a, b ) ON x.a = v.a AND x.b = v.b
Output:
a b
1 a
5 s
Based on my understanding you want write a SQL that uses a combination of two filters. Here is a simple solution that will work in any database.
Create a new column say "COLUMN_NEW" in the same table or build a temp table or a view with a new column (plus existing columns from original table).
Insert concatenated values of column a and column b in "COLUMN_NEW". Based on the example mentioned by you values in "COLUMN_NEW" will be "1a" and "5s"
Now you may have a different syntax for concat in different databases. Example concat(a,b) in SQL server.
SQL to select records from the table will be select * from table where COLUMN_NEW in ("1a",5s");

Multiple Columns in an "in" statement

I am using DB 2 and i am trying to write a query which checks multiple columns against a given set of values.Like field a, field b and field c against values x,y,z,f. One way that i can think for is writing same condition 3 times with or i.e. field a in ('x','y','z','f') or field b in .... and so on . Please let me know if there is some other efficient and easy way to accomplish this. I am looking for a query like if any of the condition is true return yes else no . Please suggest !
This may or may not work on as400:
create table a (a int not null, b int not null);
insert into a (a,b) values (1,1),(1,3),(2,3),(0,23);
select a.*
from a
where a in (1,2) or b in (1,2);
A B
----------- -----------
1 1
1 3
2 3
Rewriting as a join:
select a.*
from a
join ( values (1),(2) ) b (x)
on b.x in (a.a, a.b);
A B
----------- -----------
1 1
1 3
2 3
Assuming the column data types are the same, Create a subquery joining all the columns you want to search with your IN into one column with a union
SELECT *
FROM (
SELECT
YOUR_TABLE_PRIMARY_KEY
,A AS Col
FROM YOUR_TABLE
UNION ALL
SELECT
YOUR_TABLE_PRIMARY_KEY
,B AS Col
FROM YOUR_TABLE
UNION ALL
SELECT
YOUR_TABLE_PRIMARY_KEY
,C AS Col
FROM YOUR_TABLE
) AS SQ
WHERE
SQ.Col IN ('x','y','z','f')
Make sure to include the table key so you know which row the data refers to
You can create a regular expression that describe the set of characters and use it with xquery
Assuming you're on a supported version of the OS (tested on 7.1 TR6), this should work...
with sel (val) as (values ('x'),('y'),('f'))
select * from mytbl
where flda in (select val from sel)
or fldb in (select val from sel)
or fldc in (select val from sel)
Expanding on the above since your OP asked for "condition is true return yes else no"
Assuming you've got the key to a row to check, would 'yes' or the empty set be good enough? somekey is the key for the row you want to check.
with sel (val) as (values ('x'),('y'),('f'))
select 'yes' from mytbl
where thekey = somekey
and ( flda in (select val from sel)
or fldb in (select val from sel)
or fldc in (select val from sel)
)
It's actually rather difficult to return a value when you don't have a matching row. Here's one way. Note I've switch to 1=yes, 0=no..
with sel (val) as (values ('x'),('y'),('f'))
select 1 from mytbl
where thekey = somekey
and ( flda in (select val from sel)
or fldb in (select val from sel)
or fldc in (select val from sel)
)
UNION ALL
select 0
from sysibm.sysdummy1
order by 1 desc
fetch first row only