How to set SQL condition precedence for two different conditions - sql

I'm having two different tables 'TABLE1' and 'TABLE2'. Both of these tables have same column name - 'IS_PDF'.
RE TABLE1.IS_PDF value, I retrieved the required details using select statement where I added where clause like below.
select * from TABLE1 where TABLE1.IS_PDF = 'Y';
Similarly, I can get the values for TABLE2 as well where TABLE2.IS_PDF = 'Y'
Now, my task is to have one select statement to get the details from both tables 'TABLE1' and 'TABLE2' by setting a precedence on below condition # 1.
If TABLE1.IS_PDF = 'Y', return records matching same condition.
If TABLE1.IS_PDF = 'N' but TABLE2.IS_PDF = 'Y' then return records matching same condition.
If both have IS_PDF = 'N', return nothing.
I used below Select statement but getting Above conditions # 1 and 2 at the same time only.
select * from TABLE1, TABLE2
where
(TABLE1.IS_PDF = 'Y') or (TABLE2.IS_PDF = 'Y' and TABLE1.IS_PDF = 'N' )
Please guide on the same.
Thanks

You seem to want something like this:
select t1.*
from table1 t1
where t1.is_pdf = 'Y'
union all
select t2.*
from table2 t2
where t2.is_pdf = 'Y' and
not exists (select 1 from table1 t1 where t1.is_pdf = 'Y' and t1.?? = t2.??);
The ?? represents the column(s) used for matching between the two tables.

Thanks for the quick response.
Sample code-
select TABLE1.IS_PDF, TABLE2.IS_PDF, TABLE1.ID, TABLE1.GLOBAL_ID, TABLE1.title,
from TABLE1, TABLE2
where TABLE1.USER_ID = 82340
and TABLE1.NAME = 'INDIA'
and (TABLE1.IS_PDF = 'Y') or (TABLE2.IS_PDF = 'Y' and TABLE1.IS_PDF = 'N' )
I need to set a precedence on below condition # 1. If condition # 1 meets, it's fine. Otherwise go for #2 then #3.
If TABLE1.IS_PDF = 'Y', return records matching same condition.
If TABLE1.IS_PDF = 'N' but TABLE2.IS_PDF = 'Y' then return records matching same condition.
If both have IS_PDF = 'N', return nothing.

Related

SQL AND to see if two flags are set in two different tables?

SELECT 1 FROM
(SELECT 1 FROM mytable1 WHERE parentid = 'ID1' AND flag = 'Y') as X,
(SELECT 1 FROM mytable2 WHERE id = 'ID2' AND flag = 'Y') as Y
I'm making a query to see if two flags are set in two tables, where 'parentid' and 'id' are both primary keys. The query should return a row only if both flags are set to 'Y', or return nothing otherwise, then I do stuff with that result in my backend code.
I've tested this and it works but I feel like it looks wonky and could be optimized. Any ideas?
To get what You want:
SELECT 1
FROM mytable1 AS a, mytable2 AS b
WHERE a.parentid = 'ID1' AND a.flag = 'Y'
AND b.id = 'ID2' AND b.flag = 'Y'
But in fact, I would prefer a query with LEFT JOIN, which always gives one row, like this:
SELECT CASE WHEN a.flag = 'Y' AND b.flag = 'Y' THEN 1 ELSE 0 END AS result
FROM TABLE ( VALUES 1 ) AS always(present)
LEFT JOIN mytable1 AS a ON a.parentid = 'ID1'
LEFT JOIN mytable2 AS b ON b.id = 'ID2'
Your query is fine (although I would use CROSS JOIN. However, I would prefer a row with a specific value. I would phrase that as:
SELECT (CASE WHEN EXISTS (SELECT 1 FROM mytable1 WHERE parentid = 'ID1' AND flag = 'Y') AND
EXISTS (SELECT 1 FROM mytable2 WHERE id = 'ID2' AND flag = 'Y')
THEN 1 ELSE 0
END) as flag
You may need from dual, depending on your database.
It better to use JOIN instead of doing subqueries
SELECT mytable1.parentid, mytable2.id
FROM mytable1
JOIN mytable2 ON mytable2.flag = "Y" AND mytable1.flag = "Y"

Select query 0=0 Vs not exists

I have a table contains 100 million rows and I found the below query (A), which is taking time. I am thinking to change it to (B), is it the same?
Is this query (A) :
SELECT *
FROM tab1
WHERE code = 1
AND TYPE = 'A'
AND 0 = (SELECT Count(1)
FROM tab1
WHERE code = 1
AND tr_type = 'APPROVE'
AND security = 'Y')
Similar to this (B) ?
SELECT *
FROM tab1
WHERE code = 1
AND TYPE = 'A'
AND NOT EXISTS (SELECT 1
FROM tab1
WHERE code = 1
AND tr_type = 'APPROVE'
AND security = 'Y'
AND ROWNUM = 1)
I would recommend writing the query as:
SELECT t1.*
FROM tab1 t1
WHERE t1.code = 1 AND
t1.TYPE = 'A' AND
NOT EXISTS (SELECT 1
FROM tab1 tt1
WHERE tt1.code = t1.code AND
tt1.tr_type = 'APPROVE'
tt1.security = 'Y'
);
The changes are:
Table aliases are introduced.
All columns are qualified.
The rownum = 1 condition is removed.
The latter is between redundant and dangerous. EXISTS/NOT EXISTS is already looking for any row that matches. Including rownum could affect the optimizer.
Your first version has to find all matches in order to calculate the count. That can be much more expensive that determining that there are no matches or finding the first one.
For performance, you want indexes on tab1(code, tr_type, security) and tab1(code, type).
Yes same query.
SELECT *
FROM tab1
WHERE code = 1
AND type = 'A'
AND 0 = (SELECT Count(1)
FROM tab1
WHERE code = 1
AND tr_type = 'APPROVE'
AND security = 'Y')
If we will take output of inner query in the above query
SELECT Count(1)
FROM tab1
WHERE code = 1
AND tr_type = 'APPROVE'
AND security = 'Y'
I am assuming , Record with code = 1 , TR_TYPE = 'APPROVE' and security = 'Y' is present. So output will be 1. (Assumption is only one matching record is present).
SELECT *
FROM tab1
WHERE code =1
AND type='A'
AND 0=1 (replacing the result)
So this will not return any data as 0 is never equal to 1.
SELECT *
FROM tab1
WHERE code = 1
AND type = 'A'
AND NOT EXISTS (SELECT 1
FROM tab1
WHERE code = 1
AND tr_type = 'APPROVE'
AND security = 'Y'
AND rownum = 1)
Now checking the above query, inner query return 1 as the data with the where clause is present so result will be
SELECT *
FROM tab1
WHERE code =1
AND type='A'
AND NOT EXISTS (1)
Exists(If any record found) = true and in our case Exists(1) = true so
Not Exist (1) = false. Which denotes to false as it has 1 record , so it will also not return the data.
So if you are asking that both query will return same output. So It will return same output.
Yes the output will be same because its just the interchangeable way to use it however in terms of time complexity the second one is optimised.
Yes they are the same, however i would explicitly put an alias on the table being used in the non exists and alias the columns being compared to.Eg:
SELECT *
FROM tab1
WHERE code = 1
AND type = 'A'
AND NOT EXISTS (SELECT 1
FROM tab1 b
WHERE b.code = 1
AND b.tr_type = 'APPROVE'
AND b.security = 'Y'
AND rownum = 1)

sql case statement update based on other table

I want to update a table (table1) based on the values of one or more fields in another table (table2). I believe this should be a case statement but I'm unsure how to incorporate a case statement and an update clause based on another table in one statement. Here's what I have so far which I know does not work:
update table1 i, table2 s
set i.sales = 'F'
where s.payment = 'Y'
and i.order_no = s.order_no;
I know how to do a select based on two tables but that's not very helpful since I don't want to create a new database object - I just want to update an existing object (table1):
create or replace view merge as
select
i.order_no
, case when s.payment = 'Y'
then 'F'
end as sales
from table1 i, table2 s
where i.order_no = s.order_no;
And I know how to update WITHIN a case statement:
UPDATE table1
SET sales = (
SELECT CASE
WHEN foo = 'X'
THEN 'F'
ELSE null
END
FROM table1
)
;
I considered a where clause instead of a case statement but it ends up selecting EVERY record and the 2nd table definitely has different values in the payment field:
update t1
set sales = 'F'
where exists (select table2.payment
from table2
where table2.order_no = table1.order_no
and table2.payment = 'Y');
try this:
update table1 i
set i.sales = (select case when x.payment = 'Y'
then 'F'
else i.sales end
from table2 x
where x.order_no = i.order_no);
I don't have an oracle running right now (so I cannot check the syntax propertly), but I think you can try something like this:
update table1 i
set i.sales = 'F'
where i.order_no IN (select s.order_no from table2 s where s.payment = 'Y')
Hope it helps!

Oracle select query with dynamic in clause

I have the following query
select * from table_1
where Conditions in
(select case when check_condition = 'Y' then 'Condition_1'
else 'N/A' end as Check_condition
from table_2
WHERE id = 1122)
where table_1 contains the values in column Conditions as follows.
Condition_1,Condition_2
This works fine and return me the results.
i want to use multiple select statements inside the in clause and I did it as below.
select * from table_1
where Conditions in (
select ''''||
(select case when check_condition = 'Y' then 'Condition_1'
else 'N/A' end as Check_condition
from table_2
WHERE id = 1122)||''''||','''||
(select case when check_condition = 'Y' then 'Condition_2'
else 'N/A' end as Check_condition
from table_2 WHERE id = 1122)||''''
from dual
)
inner query ( inside the in clause) giving me the correct results as expected -
'Condition_1','Condition_2'
and when I copy paste it to the parent query it works fine and show the results.
select * from table_1 where Conditions in ('Condition_1','Condition_2')
My issue is, it is not giving any results when I used the second query. I know that sub query will return the results that should match the rows in the outer query. But it shows me empty result set.
I am using oracle 11g
Can anyone please help me out.. Thank you all in advance.
The question is a bit unclear regarding requirements. What I think you want is to select records from table1 only when:
rows match 'Condition_1' or 'Condition_2'
check_condition = 'Y'
there is a row in table2 with an ID = 1122
It's not clear from your question whether check_condition is a column or a variable, and if it is a column to which table it belongs. Consequently this solution may be wrong but it illustrates the principle.
select * from table1 t1
where t1.conditions in ('Condition_1','Condition_2')
and t1.check_condition = 'Y'
and exists
( select null from table2 t2
where t2.id = 1122 )
If this doesn't provide the solution you need please revise your question so it states the business logic you need to implement, and also includes relevant tables descriptions.
You aren't ending up with two values passed into the in clause like when you do it manually:
select * from table_1 where Conditions in ('Condition_1','Condition_2')
You're passing a single value which is the concatenation of the values:
select * from table_1 where Conditions in ('''Condition_1'',''Condition_2''')
And no condition matches that concatenated value, so you get no results. You could do something like:
select * from table_1 where Conditions in (
select case when check_condition = 'Y' then 'Condition_1' else 'N/A' end
from table_2 WHERE id = 1122
union all
select case when check_condition = 'Y' then 'Condition_2' else 'N/A' end
from table_2 WHERE id = 1122
)
Or possibly, if I follow what you're doing (which is doubtful as I'm not sure I understand your data model!):
select * from table_1 where check_condition != 'Y' or Conditions in (
select 'Condition_1' from table_2 WHERE id = 1122
union all
select 'Condition_2' from table_2 WHERE id = 1122
)
It seems like you should be able to do this more cleanly, with joins, but I think we'd need to see the structures and sample data to understand what's going on a bit more.

SQL Statement Performance Issue on Informix

I have this Informix SQL statement which takes ages to run. Does anybody see any way to optimize it so it wouldn't take so long?
SELECT * FROM OriginalTable WHERE type = 'S' AND flag <> 'S' INTO TEMP TempTableA;
SELECT * FROM OriginalTable WHERE type = 'Z' AND flag <> 'S' INTO TEMP TempTableB;
UPDATE OriginalTable SET flag = 'D' WHERE Serialnumber in
(
select Serialnumber from TempTableA
WHERE NOT EXISTS(SELECT * FROM TempTableB
WHERE TempTableB.Col1 = TempTableA.Col1
AND TempTableB.Col2 = TempTableA.Col2)
)
I have in my OriginalTable around 300 million rows, TempTableA 93K rows, and TempTableB 58K rows.
Update OriginalTable
Set flag = 'D'
Where Type = 'S'
And Flag <> 'S'
And Not Exists (
Select 1
From OriginalTable As T1
Where T1.Type = 'Z'
And T1.flag <> 'S'
And T1.Col1 = OriginalTable.Col1
And T1.Col2 = OriginalTable.Col2
)
In a similar approach as #tombom stated. Pre-query only the columns you care about to keep the temp table smaller. If you are dealing with a table of 60 columns, you are filling a whole lot more than just 3-4 columns where your primary consideration are valid serial numbers. Pre-test the query to make sure it gives you the correct set you are expecting, then apply that to your SQL-update.
So here, the inner query are the ones you DO NOT WANT... Since you were comparing against only column 1 and column 2 from this table, that's all I'm pre-querying. I'm then doing a LEFT JOIN to this inner result set on COL1 and COL2. I know, you want to EXCLUDE THOSE FOUND IN THIS result set... That's why, in the OUTER WHERE clause, I've added "AND ExcludeThese.Col1 IS NULL". So, any instances from OT1 that never existed in the subquery are good to go (via left join), and those that WERE FOUND, WILL have a match on col1 and col2, but THOSE will be excluded via the "and" clause I've described.
SELECT OT1.SerialNumber
FROM OriginalTable OT1
LEFT JOIN ( select OT2.Col1,
OT2.Col2
FROM OriginalTable OT2
where OT2.type = 'Z'
AND OT2.flag <> 'S' ) ExcludeThese
ON OT1.Col1 = ExcludeThese.Col1
AND OT1.Col2 = ExcludeThese.Col2
WHERE OT1.type = 'S'
AND OT1.flag <> 'S'
AND ExcludeThese.Col1 IS NULL
ORDER BY
OT1.SerialNumber
INTO
TEMP TempTableA;
Again, test this query by itself to make sure you ARE getting the records you expect. To help clarify the records returned, change the above select to include more columns for a mental / sanity check, such as
SELECT OT1.SerialNumber,
OT1.Col1,
OT1.Col2,
ExcludeThese.Col1 JoinedCol1,
ExcludeThese.Col2 JoinedCol2
from <keep rest of query intact>
Now, you'll be able to see the serial number and instances of those columns that would or not be joined to the "excludeThese" resultset... Try again, but remove only the
"AND ExcludeThese.Col1 IS NULL" clause, and you'll see the other lines and WHY they are being excluded -- that is if you DID have any questions to the content.
Once you are satisfied with the pre-query... which will only return the single column of SerialNumber, that can be index/optimized since you are pulling into a temp table, build an index, then apply your update.
UPDATE OriginalTable
SET flag = 'D'
WHERE Serialnumber in ( select Serialnumber from TempTableA );
I was too lazy to test with test data, but maybe this can do?
SELECT col1, col2,
CASE WHEN type = 'S' THEN 1
ELSE WHEN type = 'Z' THEN 2 END AS filteredType
FROM OriginalTable WHERE (type = 'S' OR type = 'Z') AND flag <> 'S' INTO TempTable;
UPDATE OriginalTable SET flag = 'D' WHERE Serialnumber IN
(
SELECT t1.Serialnumber FROM TempTable t1
LEFT JOIN TempTable t2 ON (t1.col1 = t2.col2 AND t1.col2 = t2.col2)
WHERE t1.filteredType = 1
AND t2.filteredType = 2
AND t2.Serialnumber IS NULL
)
That way you can omit one loading into temp table. On the other hand there will be no index on the new column filteredType.
Also I have no idea of informix. Hope it helps anyway.