Update using case with multiple tables - sql-server

I have two tables. I want to update table1 when the condition is satisfied. The condition here is to check the country in table 2 and if its Mex, then multiply rate i.e. 0.5 to the price.
I wrote the following code
UPDATE table1
SET table1.Price = (SELECT *,
CASE table2.Country
WHEN 'CANADA' THEN (1 * table2.price)
WHEN 'MEXICO' THEN (0.5 * table2.price)
ELSE 0
END AS Price_Calc
FROM table2)
FROM table1;
As I run this it gives the below error
Msg 116, Level 16, State 1, Line 12 Only one expression can be
specified in the select list when the subquery is not introduced with
EXISTS.

Try Like below
UPDATE t1
SET t1.table1.Price = (SELECT
CASE t2.Country
WHEN 'CANADA' THEN (1 * t2.price)
WHEN 'MEXICO' THEN (0.5 * t2.price)
ELSE 0
END AS Price_Calc
FROM table2 t2
WHERE t2.Id = t1.Id -- Here it is suggestion to update target
-- column based on relation if exists between
-- ur tables
)
FROM table1 t1;

Assuming Table1 and Table2 are related through IDs:
UPDATE t1 SET t1.Price = t2.Price
* CASE t2.Country
WHEN 'CANADA' THEN 1.
WHEN 'MEXICO' THEN .5
ELSE 0
END
FROM table1 t1
INNER JOIN table2 t2 ON t2.Id = t1.Id
;

Related

Multiple OR operator in Snowflake in WHERE Clause is not working

I am trying to do below
Table 1
Table 2
I am writing a query like below to ensure that if any of the NOT IN satisfies, those records should be filtered out.
SELECT * FROM TABLE1
WHERE TABLE1."DEPTID" NOT IN (SELECT TABLE2."DEPTID" FROM TABLE2)
OR
TABLE1."EMPCOUNTRY" NOT IN (SELECT TABLE2."EMPCOUNTRY" FROM TABLE2)
OR
TABLE1."EMPZONE" NOT IN (SELECT TABLE2."EMPZONE" FROM TABLE2)
But it errors out
What am I doing wrong?
Edited : Exact Query is working with nvl , but result set is not as per requirement.
SELECT * FROM TABLE1 AS T1
WHERE
(
UPPER (T1."DEPTID") NOT IN
(SELECT UPPER (nvl (T2."DEPTID", '')) FROM TABLE2 AS T2)
OR
UPPER (T1."EMPZONE") NOT IN
(SELECT UPPER (nvl (T2."EMPZONE",'')) FROM TABLE2 AS T2)
)
Result - set is not as per requirement, it should filter out if there any DEPTID in the Table2 or if there is any EMPZONE in table 2 or both etc.
What should be the best way to achieve this?
I think the issue is about NULL values coming from the subqueries. Could you try to use something like this?
SELECT * FROM TABLE1 AS T1
WHERE
(
NOT EXISTS (SELECT 1 FROM TABLE2 T2 WHERE equal_null( UPPER(T1."DEPTID"), UPPER(T2."DEPTID")) )
OR
NOT EXISTS (SELECT 1 FROM TABLE2 T2 WHERE equal_null( UPPER(T1."EMPZONE"), UPPER(T2."EMPZONE")) )
);
Also here is a sample to demonstrate why you can't use NOT IN with a subquery returning NULL values:
create table NULL_TABLE ( v varchar);
insert into NULL_TABLE values (NULL),('ABC');
create or replace table MAIN_TABLE ( v varchar);
INSERT INTO MAIN_TABLE values
('Jack'),('Joe'),('ABC');
select * from MAIN_TABLE
where v NOT IN (select v FROM NULL_TABLE);
The last query returns NULL, because we can't determine if a value does not exist in series of numbers where some of them are not known - the last WHERE clause.
When using NOT IN the subquery should not allow null values as reselut, second conditions to happen all at once should be joined with AND instead of OR.
NOT (cond1 OR cond2 OR cond3)
<=>
(NOT cond1) AND (NOT cond2) AND (NOT cond3)
De Morgan's law: "The negation of a disjunction is the conjunction of the negations"
The final query should rather be:
SELECT *
FROM TABLE1 AS T1
WHERE T1."DEPTID" NOT IN (SELECT T2."DEPTID" FROM TABLE2 T2
WHERE T2."DEPTID" IS NOT NULL)
AND T1."EMPCOUNTRY" NOT IN (SELECT T2."EMPCOUNTRY" FROM TABLE2 T2
WHERE T2."EMPCOUNTRY" IS NOT NULL)
AND T1."EMPZONE" NOT IN (SELECT T2."EMPZONE" FROM TABLE2 T2
WHERE T2."EMPZONE" IS NOT NULL);

SQL Server Update data if not exists from another table

I need to update values in a table from another table and if they don't exist it must be inserted. So I saw this example in here
UPDATE Table1 SET (...) WHERE Column1='SomeValue'
IF ##ROWCOUNT=0
INSERT INTO Table1 VALUES (...)
Now I would like to do this but instead of a where clause with a value i need to check if columns match on another table.
Something like
UPDATE t1
SET
t1.status = t2.status
FROM
table1 t1, table2 t2
WHERE t1.reference = t2.reference and t1.section = t2.section
I assume this is very bad in terms of performance and i would like to hear some other suggestions.
EDIT
Also please, if you can, i would like to hear why the approach you give is better than using a merge or a join for example. Much appreciated.
RESULT
Here is the approach i went for after checking how bad merge performance is compared to an update then insert and trying to test everyones approach this is what i decided for, basically uses a bit of everyone's answer
UPDATE C
SET
C.ConsumoWeek01 = M.ConsumoWeek01
FROM
Consumos AS C
INNER JOIN #tempTable M
ON C.Referencia = M.Referencia
AND C.UAP = M.UAP
INSERT INTO
Consumos
SELECT *
FROM #tempTable M
WHERE NOT EXISTS
(
SELECT 1 FROM Consumos C
WHERE C.Referencia = M.Referencia
AND C.UAP = M.UAP
)
Try this
UPDATE t1
SET
t1.status = t2.status
FROM
table1 t1
WHERE NOT EXISTS (SELECT 1 FROM table2 t2
WHERE t1.reference = t2.reference
and t1.section = t2.section )
use not exists
UPDATE t1
SET
t1.status = t2.status
FROM
table1 t1 where not exists( select 1 from table2 t2 where t1.=ref=t2.ref and t1.section=t2.secction)
Use merge statement:
MERGE INTO Table1 T1
USING
(
SELECT * FROM Table2
) T2
ON
(
T1.reference = T2.reference AND
T1.section = T2.section
)
WHEN MATCHED THEN
--UPDATE STATEMENT
WHEN NOT MATCHED THEN
--INSERT STATEMENT
Your method is fine just use standard explicit join syntax instead of old style with comma separate :
update t1
set . . .
from table t1 inner join
table t2
on t1.reference = t2.reference and t1.section = t2.section;

Set column status based on a match between two tables

I have two tables
T1
id
1
2
3
4
T2
id status
1 Yes
2 Yes
3 Yes
4 Yes
5 No
6 No
If I find id matched in both the tables, the status should be updated as Yes else No
One method is to use a CASE expression and EXITS with a correlated subquery that checks for the existence of a row in the other table.
UPDATE t2
SET status = CASE
WHEN EXISTS (SELECT *
FROM t1
WHERE t1.id = t2.id) THEN
'Yes'
ELSE
'No'
END;
You can update using a LEFT JOIN. If the t1.id is null, then there is no match, and you can set it to No. Otherwise, set it to yes.
UPDATE t2
SET t2.Status = CASE WHEN t1.id IS NOT NULL THEN 'Yes'
ELSE 'No'
END
FROM t2
LEFT JOIN t1
ON t1.id = t2.id
Alternatively, you can use a subquery within the CASE
UPDATE t2
SET t2.Status = CASE WHEN t2.id IN (SELECT id FROM t1) THEN 'Yes'
ELSE 'No'
END

SQL Server - Update with FROM and JOIN not considering all the rows that satisfies JOIN condition

So, I have two tables T1 and T2 where in T1 has Primary Key Column "A" with all Unique Values and T2 has Foreign Key "A" as shown below.
T1
---
A ConditionMet
1 0
2 0
T2
---
A B C
1 10 5
1 20 20
2 10 10
2 30 0
Now, I want to update "ConditionMet" column of T1 table to "1" whenever Value of Column B in T2 is equal to Value of Colum C (for one specific value in A). My update query is something like below
UPDATE t1
SET
t1.ConditionMet =
(CASE WHEN t1.ConditionMet = 0 AND t2.B = T2.C THEN 1 END)
FROM
T1 t1
INNER JOIN T2 t2
WHERE
t1.A = t2.A
Here my problem is that it is not looping through all the rows of T2 but its going through first record with the value of "A". Suppose if we take value "1" in T1, it would only look at first row (1 10 5) but not at the second row (1 20 20) of T2. Please help me understand whats wrong with this query. Thanks in advance!!
try this....
UPDATE t1
SET
t1.ConditionMet = CASE WHEN t1.ConditionMet = 0 AND t2.B = T2.C
THEN 1
END
FROM T1 t1
INNER JOIN T2 t2 ON t1.A = t2.A
WHERE NOT EXISTS (SELECT 1
FROM T2
WHERE t1.A = A
AND B <> C)
The problem is that when it fits both conditions, you can't be assured the order of which update will occur last. This should do what you need:
UPDATE t1
SET
t1.ConditionMet = CASE WHEN T2.a IS NULL THEN 0 ELSE 1 END
FROM
T1 t1
LEFT JOIN T2 t2
WHERE
t1.A = t2.A
and t2.B = t2.C
The problem is you are expecting a first, second or last result, but defining no order. Why should the row for A = 1 where B = 10 be first, and B = 20 be second? It is actually the update SQL Server does last that is the one that sticks, but even so, you can't fix your query with just an ORDER BY
You need to write a completely deterministic query, that is to say write a select that only returns 1 row per row being updated, and this can reliably be used as an update. I don't think I would go as far as Hugo Kornelis, who has called for UPDATE...FROM to be deprecated, but you do need to understand it if you are to not be caught out by it.
So if you are looking to update rows in T1 where there is at least one row in T2 where B = C, then I would use:
SELECT T1.A, T1.ConditionMet, NewConditionMet = ISNULL(T2.NewConditionMet, 0)
FROM T1
OUTER APPLY
( SELECT TOP 1 NewConditionMet = 1
FROM T2
WHERE T2.A = T1.A
AND T2.B = T2.C
) AS T2;
For your sample data this will return
A ConditionMet NewConditionMet
1 0 1
2 0 1
All you then need to do is adapt your SELECT into an UPDATE:
UPDATE T1
SET ConditionMet = CASE WHEN T1.ConditionMet = 0 THEN ISNULL(T2.NewConditionMet, 0) END
FROM T1
OUTER APPLY
( SELECT TOP 1 NewConditionMet = 1
FROM T2
WHERE T2.A = T1.A
AND T2.B = T2.C
) AS T2;
N.B. This is a very rare occasion when I deem that it is okay to use TOP without ORDER BY, since I am only returning a constant value, 99% of the time is not okay to use TOP without ORDER BY
WORKING EXAMPLE
DECLARE #T1 TABLE (A INT, ConditionMet BIT);
DECLARE #T2 TABLE (A INT, B INT, C INT);
INSERT #T1 VALUES (1, 0), (2, 0), (3, 0), (4, 1);
INSERT #T2 VALUES (1, 10, 5), (1, 20, 20), (2, 10, 10), (2, 30, 0), (3, 0, 1), (4, 0, 0);
SELECT * FROM #T1;
UPDATE T1
SET ConditionMet = CASE WHEN T1.ConditionMet = 0 THEN ISNULL(T2.NewConditionMet, 0) END
FROM #T1 AS T1
OUTER APPLY
( SELECT TOP 1 NewConditionMet = 1
FROM #T2 AS T2
WHERE T2.A = T1.A
AND T2.B = T2.C
) AS T2;
SELECT * FROM #T1;

SQL Server stored procedure select, exists, multiple tables

Any method to do this?
Table1
1
2
3
4
5
Table2
3 (with the condition)
4 (without the condition)
I want to:
Select all records from Table1 if it exists in Table 2, where...(condition)
Select all records from Table1 if it not exists in Table2
Combine both select results. Sort all results with their created date.
For example, the result should be:
Result
1
2
3
5
Hopefully this can help.
SELECT t1.* from table1 t1
JOIN table2 t2
ON t1.ID = t2.ID
UNION ALL
SELECT t1.* from table1 t1 where ID in
(
SELECT t2.ID from table1 t1 except Select t2.ID from table2 t2
)
ORDER BY t1.CreatedDate
You can achieve this by doing:
SELECT t1.id
FROM Table1 t1
LEFT JOIN Table2 t2 on t1.id = t2.id
WHERE condition OR t2.id IS NULL
ORDER BY t1.CreatedDate;
See fiddle (I assumed condition to be t2.id!=4, but it can be anything else depending on other data in your tables).
There could be multiple solution.
One way
we can get the result set using two different queries and at last combine both of the result-set using UNION
Another way,
First statement is saying that get all the result set from TABLE1 if it exists in TABLE2 as well with some criteria (condition in where clause)
means using INNER JOIN we can achieve this
Second statement is saying get all the result set from TABLE1 which are not present in TABLE2
means along with INNER JOIN ed query also include the TABLE1's data if not present in TABLE2
here we can take the help of LEFT OUTER JOIN (taking TABLE1 on the left side)
Assumption: (condition: t1.Id != 4)
Let's try to understand the query using both of the above mentioned ways
---- -- --Step1 Create table and insert records
---- create table1 with Id int identity columsn
--CREATE TABLE Table1 (Id INT IDENTITY(1,1), CreatedDate smalldatetime default(getdate()));
--go
---- insert 1st 5 integers into Table1
--INSERT INTO Table1 DEFAULT VALUES
--go 5
---- create Table2 with Id int column
--CREATE TABLE Table2 (Id INT , CreatedDate smalldatetime default(getdate()));
--go
---- insert records 3,5 into Table2
--INSERT INTO Table2(Id) VALUES (3), (4);
-- -- -- Solution: one way
; WITH cteMyFirstResult AS
(
-- 2.1. Select all records from Table1 if it exists in Table 2, where...(condition)
SELECT
Id, CreatedDate
FROM Table1 AS t1
WHERE t1.Id IN (SELECT Id FROM Table2 AS t2)
AND t1.Id != 4 -- assumption it can be any condition
),cteMySecondResult AS (
-- 2.2. Select all records from Table1 if it not exists in Table2
SELECT
Id, CreatedDate
FROM Table1 AS t1 WHERE t1.Id NOT IN (SELECT Id FROM Table2 AS t2)
)
-- 2.3. Combine both select results. Sort all results with their created date.
SELECT
Id, CreatedDate
FROM cteMyFirstResult
UNION
SELECT
Id, CreatedDate
FROM cteMySecondResult
ORDER BY CreatedDate;
-- -- Solution: Another way (with bug)
SELECT t1.Id, t1.CreatedDate
FROM Table1 AS t1
LEFT JOIN Table2 AS t2 on t1.id = t2.id
WHERE t1.Id != 4
Order by T1.CreatedDate;
-- in this query we are using the criteria after doing the join operation.
-- thus after filtering out the result set based on JOIN Condition this condition will get applied
-- and if there is any null record in the Table1 for column Id (used in join) will not come in the final result-set
-- to avoid this we can include NULL check along with our criteria
-- -- Solution: Another way
SELECT t1.Id, t1.CreatedDate
FROM Table1 AS t1
LEFT JOIN Table2 AS t2 on t1.id = t2.id
WHERE ( t1.Id != 4 ) OR t1.Id IS NULL -- include all your criteria within small-barcket)
Order by T1.CreatedDate;
Thanks for all responses.
I come out with the answer I want:
SELECT *
FROM Table1 t1
WHERE NOT EXISTS(SELECT 1 FROM Table2 t2
WHERE t1.ID = t2.ID
AND t2.CIF_KEY = #CifKey
AND t2.STATUS <> ''3'')
AND (condition in where clause)

Resources