How to set nulls the values of column B and column C if they already exist on column A sql? - sql

Select BillName as A, ConsigneeName as B, ShipperName as C
from Sum_Orders
where (OrderStatus in ('Complete','Invoiced')
)
and
OrderPeriodYear IN (
(
YEAR(GETDATE())-1
)
)
Group by billname,ConsigneeName,ShipperName
I'm having duplicates in A, B, C (which is expected)
I'm trying to make a condition to
keep the value in A and set to nulls the values that repeat in B OR C
IF A = B or C then keep A and SET B or C to NULLS
Thank you, guys, :D

Is this what you want?
update t
set B = (case when B <> A then B end),
C = (case when C <> A then C end)
where B <> A or C <> A;

If you have to do this inline the perhaps a case will help.
Select Billname AS A,
CASE WHEN ConsigneeName = Billname THEN NULL ELSE ConsigneeName END,
CASE WHEN ShipperName = Billname THEN NULL ELSE ShipperName
from Sum_Orders etc ...
If the table is big, this maybe expensive on the query and pushing this logic into the query itself might be better.

If a,b and c has same value, b and c should set null, so:
Update tablename
Set B = if(A=B, null, B) , C=if(A=C, null, C)
-- where A=B or A=C
You can use 'where' if optimization is interesting!
If you're going to 'Select' value:
Select A, if(A=B, null, B) as B , if(A=C, null, C) as C from tablename

Related

How can I improve this conditional UPDATE query?

I have a table t with several columns, let's name them a, b and c. I also have a state column which indicates the current state. There is also an id column.
I want to write the following query: update column a always, but b and c only if the application state is still equal to the database state. Here, the state column is used for optimistic locking.
I wrote this query as following:
UPDATE t
SET a = $a$,
b = (CASE WHEN state = $state$ THEN $b$ ELSE b END),
c = (CASE WHEN state = $state$ THEN $c$ ELSE c END)
WHERE id = $id$ AND
(
a != $a$ OR
b != (CASE WHEN state = $state$ THEN $b$ ELSE b END) OR
c != (CASE WHEN state = $state$ THEN $c$ ELSE c END)
)
Here, $id$, $a$, ... are input variables from the application. The second part of the WHERE clause is to avoid updates which do not effectively update anything.
This query works as expected, but is very clumsy. I am repeating the same condition several times. I am looking for a way to rewrite this query in a more elegant fashion. If this was a simple SELECT query, I could do something with a LATERAL JOIN, but I cannot see how to apply this here.
How can I improve this query?
Split the query in two:
UPDATE t
SET a = $a$
WHERE id = $id$
UPDATE t
SET b = $b$,
c = $c$
WHERE id = $id$ AND
state = $state$
If you need atomicity, wrap in a transaction.
This seems a bit cleaner(untested):
WITH src AS (
SELECT $a$ AS a
, (CASE WHEN state = $state$ THEN $b$ ELSE b END) AS b
, (CASE WHEN state = $state$ THEN $c$ ELSE c END) AS c
FROM t
WHERE id = $id$
)
UPDATE t dst
SET a=src.a, b=src.b, c=src.c
FROM src
WHERE dst.id = src.id
AND (src.a, src.b, src.c) IS DISTINCT FROM (dst.a, dst.b, dst.c)
;
EDIT: It Took me a while to realize my fault here: The question obviously targets at a single update, while my answer tried to update many rows. However, if you need to execute this Update for a set of rows you could:
Insert the needed parameters in a temporary table
Join that table within the "t2" subquery
Select it's columns (e.g. tempTable.b As tempB)
Replace the Parameters (e.g. $b$ -> t2.tempB)
.
UPDATE t
SET a=source.a,
b=source.b,
c=source.c
FROM
(
SELECT
id,
a,
(CASE WHEN UpdateCondition THEN $b$ ELSE b END) AS b,
(CASE WHEN UpdateCondition THEN $c$ ELSE c END) AS c
FROM
(
SELECT state = $state$ As UpdateCondition, * FROM t
) As t2
WHERE
id = $id$ AND
(
a != $a$ OR
b != (CASE WHEN UpdateCondition THEN $b$ ELSE b END) OR
c != (CASE WHEN UpdateCondition THEN $c$ ELSE c END)
) AS source
WHERE t.id=source.id;
The Sub query for t2 gives you your state Condition and executes the calculation for it only once per row.
The subquery for "source" gives you the mapped values and filters those without changes.
The only filter you need is on ID = $id
The case statement says don't change it in the update if the state doesn't match, so you don't need to filter it.
EDIT
where Id = $id and a !=$a
Or (state = $state and (b !=b or c!= $c))
If you do any more than that then"always update a" will not necessary be true.
3rd attempt checks for the possibility of a remaining the same, but b or c updating.

sql function case returns more than one row

Going to use this query as a subquery, the problem is it returns many rows of duplicates. Tried to use COUNT() instead of exists, but it still returns a multiple answer.
Every table can only contain one record of superRef.
The below query I`ll use in SELECT col_a, [the CASE] From MyTable
SELECT CASE
WHEN
EXISTS (SELECT 1 FROM A WHERE
A_superRef = myTable.sysno AND A_specAttr = 'value')
THEN 3
WHEN EXISTS (SELECT 1 FROM B
INNER JOIN С ON С_ReferenceForB = B_sysNo WHERE C_superRef = myTable.sysno AND b_type = 2)
THEN 2
ELSE (SELECT C_intType FROM C
WHERE C_superRef = myTable.sysno)
END
FROM A, B, C
result:
3
3
3
3
3
3...
What if you did this? Because Im guessing you are getting an implicit full outer join A X B X C then running the case statement for each row in that result set.
SELECT CASE
WHEN
EXISTS (SELECT 1 FROM A WHERE
A_superRef = 1000001838012)
THEN 3
WHEN EXISTS (SELECT 1 FROM B
INNER JOIN С ON С_ReferenceForB = B_sysNo AND C_superRef = 1000001838012 )
THEN 2
ELSE (SELECT C_type FROM C
WHERE C_superRef = 1000001838012)
END
FROM ( SELECT COUNT(*) FROM A ) --This is a hack but should work in ANSI sql.
--Your milage my vary with different RDBMS flavors.
DUAL is what I needed, thanks to Thorsten Kettner
SELECT CASE
WHEN
EXISTS (SELECT 1 FROM A WHERE
A_superRef = 1000001838012)
THEN 3
WHEN EXISTS (SELECT 1 FROM B
INNER JOIN С ON С_ReferenceForB = B_sysNo AND C_superRef = 1000001838012 )
THEN 2
ELSE (SELECT C_type FROM C
WHERE C_superRef = 1000001838012)
END
FROM DUAL

how can get the result and appoint into next

1 )
SELECT A.SSID FROM T_TABLE_1 A, T_TABLE_2 B WHERE A.SSID = B.SSID AND B.NUMBER = '123456';`
2)
delete from T_TABLE_3 where ssid='139729252';
delete from T_TABLE_4 where ssid='139729252';
Result of 1) is a SSID, eg: '139729252' ,how can I use the result of 1) into 2), no need to copy and paste every time? thanks.
Just use IN operator if you expect more than 1 record to be retrieved using your select statement. Else you can use = operator.
Ex:
delete from T_TABLE_3
where ssid=(SELECT A.SSID FROM T_TABLE_1 A, T_TABLE_2 B WHERE A.SSID = B.SSID AND B.NUMBER = '123456');
or
delete from T_TABLE_3
where ssid IN (SELECT A.SSID FROM T_TABLE_1 A, T_TABLE_2 B WHERE A.SSID = B.SSID AND B.NUMBER = '123456');
delete from T_TABLE_3 where ssid in ( select a.ssid t_table_1 a, t_table_2 b where a.ssid=b.ssid and b.number='123456');
or

Oracle SQL division

I have
SELECT
COUNT(*) AS a,
SUM(CASE WHEN r.hn IS NOT NULL THEN 1 ELSE 0 END) AS b,
SUM(CASE WHEN r.hn IS NULL THEN 1 ELSE 0 END) AS c,
( ____ / ____ ) AS d
FROM
x
LEFT JOIN (SELECT DISTINCT xn FROM yn) r ON x.xn = y.xn;
I need the blanks on line 4 to be the values set to 'a' and 'c', I'm not sure what the correct syntax is though.
You can't refer to column aliases in the same level of the query (except in the order by clause), so you have to either repeat the original expression as in #juergend's answer, or use an inline view:
SELECT a, b, c, a/c AS d
FROM (
SELECT
COUNT(*) AS a,
SUM(CASE WHEN y.hn IS NOT NULL THEN 1 ELSE 0 END) AS b,
SUM(CASE WHEN y.hn IS NULL THEN 1 ELSE 0 END) AS c
FROM x
LEFT JOIN (SELECT DISTINCT xn FROM yn) y ON y.xn = x.xn
);
For complicated expressions this is a bit simpler and easier to maintain - if the expression changes you only have to modify it in one place, reducing the risk of a mistake.
If you're trying to make d the ratio of nulls to the total then you just need the division reversed, as c/a; and if you wanted the percentage then100*c/a, possibly rounded or truncated to a certain precision.
And as Clockwork-Muse mentioned, since count() ignores nulls, you coudl use that instead of the two sum() calls:
SELECT a, b, c, a/c AS d
FROM (
SELECT
COUNT(*) AS a,
COUNT(y.hn) AS b,
COUNT(*) - COUNT(y.hn) AS c
FROM x
LEFT JOIN (SELECT DISTINCT xn FROM yn) y ON y.xn = x.xn
);
... or you could calculate c in the outer query too, as (b - a), though that makes the d calculation messier.
The correct syntax is to rewrite the statements again. You can't re-use alias names in the select clause.
SELECT COUNT(*) AS t,
count(r.hn) AS c,
SUM(case when r.hn IS NULL then 1 end) AS u,
count(r.hn) / SUM(case when r.hn IS NULL then 1 end) AS p
FROM h
LEFT JOIN (SELECT DISTINCT hn FROM r) r ON h.hn = r.hn;

Updating a COLUMN Based on a Field Value

All, I have four columns (A INT, B INT, C INT and D VARCHAR(1)) in a table TableName. I want to move the values from column C to either A or B based upon the value in D. So if D = 'A' I want to move the value in C to A. How can I achieve this?
DECLARE #Column COLUMN;
UPDATE TableName
SET (#Column =
(CASE
WHEN D = 'A' THEN A
WHEN D = 'B' THEN B
END)) = C;
Note. I understand the above is madness, but I am trying to express the problem as clearly as possible. I have also exhausted my search for answers. I am not new to SQL but this one has me stumped. Any help is as always, most appreciated.
Here,
UPDATE TableName
SET A = (CASE WHEN D = 'A' THEN C ELSE A END),
B = (CASE WHEN D = 'B' THEN C ELSE B END)
in this case, only 1 column will be changed since D has only one value at a time.
SQLFiddle Demo