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.
Related
I am creating a fairly complex SELECT query with joins across multiple tables and views. The primary table is joined with several secondary tables. The result set of rows needs to be further split up based on approximately 25 CASE statement conditions for two scenarios:
Where one column is checked for a particular value based on which a new row has to be created with some unique values for a few columns;
Where two columns using an AND condition have to be checked in order to create a new row
Here is an example:
SELECT col1, col2, col3, col25, col26, col27, col48
FROM T1
INNER JOIN Table2 on Table2.col1 = T1.col1
INNER JOIN Table3 on Table3.col2 = T1.col1
Col25 is being compared with a column (Table2.ColA) from an INNER JOIN and this is where my CASE conditions are required, approximately 25 of them:
CASE WHEN Table2.ColA = 'W-%' THEN Col25 = 'WATER'
and I also need to insert one or more additional rows where
Col25 = 'REL',
Col26 = 5000,
Col27 = 'M'
Basically what the CASE expression is supposed to do is replace a value for the resulting row from my SELECT statement, and also insert one or more rows with different values for Col25, Col26, Col27 while keeping other column values the same as those returned by the SELECT statement.
I understand that this might involve more than just my select statements and could require traversing a cursor containing the result of my primary query in order to create the news rows. Is this the only route available to me? Any help or pointers would be much appreciated.
Option 1
with q as ( -- your query
select ...
)
select * from q
union all
select ... from q
where <conditions requiring a copy>
Option 2
inner join (values ('org'), ('new')) copy(flag) on flag = 'org' or
flag = 'new' and 1 = case
when col25 = 'A' then 1
when col25 = 'B' and col26 = 'X' then 1
...
end
For output columns you'd need similar cases to distinguish between original row and new row.
I have a question about my sql query. I need to exclude all the rows that have the value 'f' in it.
I have tried doing
SELECT * FROM table WHERE type NOT IN ('f')
But this doesnt seem to be working. Any help would be appreciated
EDIT: sorry for being unclear,
The problem I have is that theres multiple rows like this
name type
test1 f
test1 l
If i would use this query it would still return me test1 but what I want is that it returns NULL. Can this be done?
You apparently want to see rows where no other rows for the same name has the value f
select t1.*
from the_table t1
where not exists (select *
from the_table t2
where t1.name = t2.name
and t2.type = 'f');
If no such row exists the query will not return null it will simply return no rows at all.
Use the HAVING clause :
SELECT s.name
FROM YourTable s
GROUP BY s.name
HAVING COUNT(CASE WHEN s.type = 'f' THEN 1 END) = 0
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!
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.
I have a pretty poorly performing query (which I inherited) and I'm not too sure how to optimize it... As far as I understand it's setting the value of a 2nd column as the value of the 1st column PLUS a value from another table, where a relationship is found.
update table1 set
col2 = col1 || coalesce ((
select table2.the_column_wanted from table2 where table2.fk = table1.pk and
table2.flag = 'Y'))
where flag = 'Y' and pk in ( select distinct fk from table2 );
The exact speed issue depends on the characteristics of your table, but in general I would look into MERGE for this sort of problem. Something like this:
MERGE INTO table1 USING table2
ON table1.pk = table2.fk and
table1.flag = 'Y' and
table2.flag = 'Y'
WHEN MATCHED THEN UPDATE SET
table1.col2 = table1.col1 || table2.the_column_wanted;
The query as written is very questionable. You should probably take a good look at all of the code you have "inherited."