convert if-else statement inside a cursor in a set-based approach - sql

I have a script containing a cursor with if-else statement, but it takes too much times to browse the table. (a table with 79000 rows takes 1h).
So i need to convert it in a set-based approach.
The if statement is
IF (
SELECT count (b.key)
FROM general..ean a,
general..mainframe b,
general..hope c
WHERE a.ean = #ean
AND a.c_suppression = '0'
AND a.key = b.key
AND b.key = c.key
AND c.canal = #canal
) = 0
where #ean and #canal are value retrieved in each row with the cursor. The table browsed is tmp_day_house_info_corporate.
So i need to retrieve all rows from tmp_day_house_info_corporate, for which #info and #canal in the if statement retrieve 0.
Thank you for any help.

SELECT *
FROM tmp_day_house_info_corporate
WHERE not exists(
SELECT b.key
FROM general..ean a,
general..mainframe b,
general..hope c
WHERE a.ean = tmp_day_house_info_corporate.ean
AND a.c_suppression = '0'
AND a.key = b.key
AND b.key = c.key
AND c.canal = tmp_day_house_info_corporate.canal
)

Count is highly ineffective when checking if record exists, you would be much better with not exists
IF NOT EXISTS (
SELECT *
FROM general..ean a,
general..mainframe b,
general..hope c
WHERE a.ean = #ean
AND a.c_suppression = '0'
AND a.key = b.key
AND b.key = c.key
AND c.canal = #canal
)
If this is till to slow show your full query and maybe it will be possible to make it set-based.

Related

SQL replase empty row to 0

I am trying to get a list of locks on ORACLE. When there are no locks, an empty string is returned. How to make it output 0 if there are no rows, and output the required result if there are rows?
SELECT (b.seconds_in_wait) as TIME
FROM sys.v_$session b, sys.dba_blockers c, sys.dba_lock a
WHERE c.holding_session = a.session_id AND c.holding_session = b.sid and (username like '%MOBILE%');
I don't even know where to look for the answer)
Left join your sql to a dummy row and handle null with Nvl() function
WITH
dummy AS
( Select 0 "DUMMY" From Dual),
blocked AS
( -- your SQL using Joins
SELECT b.SECONDS_IN_WAIT "A_TIME"
FROM sys.v_$session b
INNER JOIN sys.dba_lock a ON(a.SESSION_ID = b.SID)
INNER JOIN sys.dba_blockers c ON(c.HOLDING_SESSION = b.SID)
WHERE b.username LIKE('%MOBILE%')
)
SELECT Nvl(b.A_TIME, 0) "A_TIME"
FROM dummy
LEFT JOIN blocked b ON(1 = 1)
Use the below by creating a virtual row using DUAL
WITH SUB_QUERY AS(
SELECT (b.seconds_in_wait) as TIME
FROM sys.v_$session b, sys.dba_blockers c, sys.dba_lock a
WHERE c.holding_session = a.session_id AND c.holding_session = b.sid
and(username like '%MOBILE%'))
select * FROM SUB_QUERY
union all
select 0 FROM DUAL
where NOT EXISTS (SELECT 1 FROM SUB_QUERY);
I would simply query the dual table and outer join the wait time query to it, like so:
WITH wait_time AS
(SELECT (b.seconds_in_wait) AS TIME
FROM sys.v_$session b,
sys.dba_blockers c,
sys.dba_lock a
WHERE c.holding_session = a.session_id
AND c.holding_session = b.sid
AND (username LIKE '%MOBILE%'))
SELECT NVL(wt.time, 0)
FROM dual
LEFT OUTER JOIN wait_time wt ON 1=1;
That way, you'll always get at least one row returned, and you're only querying the wait time query once.

Translation of a character to another SQL Server

I want to translate a character from A - B but also in the same query I want to translate B - C if is found on a list. Let's say we have word "Apple" that gets translated to "Orange" but "Orange" it is also on the list and it gets translated to "Coconut", so the final result would be "Coconut". Is this possible ?. I do not want to use a cursor but i just can't find the answer..
update tableA
set Value = b.TargetValue
from tableA a
join
tableB b on b.SourceValue = a.Value
from my TableA i have let's say a list of fruits for this example i just have the fruit "Apple" on tableA but in tableB i have a translation for that word to "Orange", but also in the same tableB i have a translation for "Orange" to "Coconut" so i would expect to have as final result "Coconut". Does that help? it's my first time sorry if i didn't explain well.
EDIT
I have created a function for this. Hope it helps someone else with the same problem.
CREATE FUNCTION [dbo].[FunctionName]
(
#sourceValue varchar(11)
)
RETURNS varchar(11)
AS
BEGIN
declare #targetId varchar(11) = (select TargetID from tableWithValues where
SourceID = #sourceValue)
if #targetId is not null and #targetId <> #sourceValue
begin
set #targetId = dbo.FunctionName(#targetId)
end
else
begin
return #sourceValue
end
return #targetId
end
I would suggest you get a final dataset by joining both fruit tables, so when you get your final fruit just join that dataset(cte or temp table) with the table you want to update.
Hope this approach helps you solve the problem.
I don't think that there is general solution for this, but there is a work-around if there are reasonable limits to the number of substitutions.
For example, the A => B => C you describe has two levels of substitution. If the max number of levels is e.g. 5 you can code like this:
update tableA
set Value = case when b5.TargetValue is not null then b5.targetValue
else when b4.TargetValue is not null then b4.TargetValue
else when b3.TargetValue is not null then b3.TargetValue
else when b2.TargetValue is not null then b2.TargetValue
else when b1.TargetValue is not null then b1.TargetValue
else b0.TargetValue end case
from tableA a
join
tableB b0 on b0.SourceValue = a.Value
left outer join tableB b1 -- outer join for no sub
on b1.SourceValue = b0.TargetValue
left outer join tableB b1
on b2.SourceValue = b1.TargetValue
left outer join tableB b1
on b3.SourceValue = b2.TargetValue
left outer join tableB b1
on b4.SourceValue = b3.TargetValue
left outer join tableB b1
on b5.SourceValue = b4.TargetValue
Here, 5 levels are supported: A =>B =>C =>D =>E =>F. If you have a situation where 6 levels are needed (e.g F => G) then it won't happen, and the result will be F.
Note that the order of the when bx.TargetValue is not null statements is important.

update multiple rows with joins

I have this query in postgresql:
select *
from A s
join B m on (s.id=m.id)
where m.key=4 and s.ran=some_input_from_user
This gives me all the rows that I need to update.
I want to set A.value to be 90 for all these rows.
It doesn't look like a standart update query
if I do...
Update A set value=90 where.....
then I can't do the join.
any ideas how to do it?
This is the basic update syntax for PostgreSQL where you are updating based on a join to another table:
update A s
set
value = 90
from B m
where
s.id = m.id and
m.key = 4 and
s.ran = some_input_from_user
The trick is you never use the alias in the lvalue for the set commands. In other words, value = 90 is not s.value = 90. It seems minor, but I'm pretty sure it will prevent your query from working. The rationale is if you are updating table A (alias s) then any fields you are updating are, de-facto, from table A -- no need to alias them, and to allow aliases would almost imply you could update something other than A with this statement, which you cannot.
You can definitely use them in the rvalues, so this would certainly be okay (if it were your desire to update A based on B):
update A s
set
value = m.salary * s.commission
from B m
where
s.id = m.id and
(s.value is null or
s.value != m.salary * s.commission)
Here is the query:
update a set value = 90
where exists (
select 1 from b
where a.id = b.id and b.key=4
and a.ran=some_input_from_user);
The above query will eliminate the requirement of reading table a twice.
Also you can use this query:
update a set value = 90
where a.id in
(select b.id from b
where a.id = b.id and b.key = 4
and a.ran=some_input_from_user);
TRY THIS
UPDATE A
SET A.VALUE = 90
from A
join B m on (A.id=m.id)
where m.key=4 and s.ran=some_input_from_user

Update a table column from the column of another table in MsAccess

In Ms Access 2010 I have two identical tables, say A and B.
The structure of each table is
key | text_column1 | text_column2
I have to update the rows of table A such that if for a given row in A the two text columns are empty, then the value from table B must be given.
Looking around, I tried
UPDATE A as a
SET a.text_column1 = (SELECT b.text_column1 FROM B AS b WHERE b.key = a.key),
a.text_column2 = (SELECT b.text_column2 FROM B AS b WHERE b.key = a.key)
WHERE a.text_column1 IS NULL and a.text_column2
Needless to say that the previous query doesn't work. I got error 3073 (2010)
You can use Access's UPDATE .. JOIN syntax:
UPDATE A as a
INNER JOIN B as b
ON a.Key = b.Key
SET a.text_column1 = b.text_column1,
a.text_column2 = b.text_column2
WHERE a.text_column1 IS NULL
AND a.text_column2 IS NULL;
Another option is to use Dlookup:
UPDATE A
SET text_column1 = Dlookup("text_column1", "B", "Key = "& A.Key),
text_column2 = Dlookup("text_column2", "B", "Key = "& A.Key)
WHERE a.text_column1 IS NULL
AND a.text_column2 IS NULL;
Try this
UPDATE A
SET A.text_column1 = (SELECT b.text_column1 FROM B AS b WHERE b.key = A.key),
A.text_column2 = (SELECT b.text_column2 FROM B AS b WHERE b.key = A.key)
WHERE A.text_column1 IS NULL and A.text_column2 IS NULL;

SQL: Can I negate a condition in a where clause?

I want to check if a boolean is true, then decide in the WHERE clause what condition to use.
Say the boolean variable is #checkbool:
SELECT *
FROM TableA A
WHERE
--if #checkbool is true, run this
A.Id = 123
--if #checkbool is false, run this
A.Id <> 123
Is there a way to negate a condition? Like in C++ you can do if !(condition).
If not, what is the best way to solve this problem?
Thank you!
SQL's equivalent of ! in C is NOT. However, in your case you want something else: you need to build a condition that decides between the two choices based on the value of #checkbool, like this:
SELECT *
FROM TableA A
WHERE ( (#checkbool) AND (A.Id = 123))
OR ((NOT #checkbool) AND (A.Id <> 123))
Here is one solution:
IF #Checkbool = 1
SELECT * FROM Table A WHERE A.Id = 123
ELSE
SELECT * FROM Table A WHERE A.Id <> 123
Here is another using just the WHERE Clause:
SELECT *
FROM Table A
WHERE
(#Checkbool = 1 AND A.Id = 123)
OR
(#Checkbool = 0 AND A.Id <> 123)
Everything you put in the where clause needs to be in the form of an expression. Thus, the solution in this case is to write the condition in the form of an expression.
Hope this helps. :)
select *
from TableA A
where
(#checkbool = 1 and A.Id = 123) or
(#checkbool = 0 and A.Id <> 123)
If checkbool is a coumn, then something like this will do.(Not in proper SQL syntax)
WHERE (A.ID=123 AND A.checkbool=TRUE) OR (A.ID!=123 AND A.checkbool=TRUE)
If checkbool is not a cloumn, replace A.checkbool with value of checkbool.
here is the correct SQL
WHERE ((checkbool) AND (A.Id = 123))OR ((NOT checkbool) AND (A.Id <> 123))
You can use IN clausule or even the != operator, like:
A.Id NOT IN (123,x,y,z);
or
A.Id != 123;