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
Related
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.
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
I have a select query returning two results and what I want is to save them in a table type of variable. This is how I am doing it:
declare #CompletedTotalValues table (CMedian int, CPerc int);
update #CompletedTotalValues set CMedian = t.CMed, CPercentile = t.CPerc
from(
Select CMed = dbo.median(case when cr.Priority = 1 then cr.Days else null end),
CPerc = dbo.Percentile90(case when cr.Priority = 1 then cr.Days else null end)
from A a inner join B b on b.Id = a.Id
where b.StatusId = 3
) t;
Here, when I run the subquery, I see CMed is 25 and CPerc is 43. However, when I execute Select * from #CompletedTotalValues, it is returning both column blank (no value it shows). Where is my mistake? Any advice would be appreciated
Why not just insert the data in the first place?
DECLARE #CompletedTotalValues TABLE (CMedian INT, CPerc INT);
INSERT #CompletedTotalValues (CMedian, CPerc)
SELECT CMed = dbo.median(CASE WHEN cr.Priority = 1 THEN cr.Days END),
CPerc = dbo.Percentile90(CASE WHENn cr.Priority = 1 THEN cr.Days END)
FROM A
INNER JOIN B ON b.Id = a.Id
WHERE b.StatusId = 3;
If I go by the query you have shared, you are trying to update without even inserting any data in the table variable at first place. Is it?
I'm trying to write a script that counts results based on 2 fields matching - but not matching like identically, but where the values re-occur throughout the table.
For example, I want to find where Field A and Field B = x & y, respectively (and count those results) however, field A isn't always X and field B isn't always Y. Also, Field A and Field B values are unknown. Here's what I've written so far:
select a.fielda, b.fieldb, count (*)
from tableA a
join tableB b
on a.fieldd = b.fieldd
where a.fielda = b.fieldb --I know this is a problem, just for notes on what I'm trying to accomplish.
group by b.fieldb, a.fielda
order by b.fieldb.
I'm a newb here so any help will be greatly appreciated. Thank you in advance.
SELECT SUM(CASE WHEN a.fielda = b.fieldb THEN 1 ELSE NULL END) AS MatchCount
, SUM(CASE WHEN a.fielda = x and b.fieldb = y THEN 1 ELSE NULL END) AS XYCount
, COUNT(*) AS FieldDMatchCount
FROM tableA a
JOIN tableB b
ON a.fieldd = b.fieldd
Tobsey had it correct for me. I didn't think that by selecting the two fields that I was already finding records where those two fields existed together...brain fart, I guess. Thank you for the help!
my question is, is it possible to select certain rows in a table according to a comparison rule without removing anything from the result. To clarify what i want to to imagine following example.
i have a table with two values,
A | B | C
1 0 hey
1 1 there
2 1 this
3 0 is
3 1 a
4 0 test
now i want to select the rows that have a 0 in the B column, and an a in the C column without removing the results that don't have a 0 in column B but the same value in column A.
For that i could do a
select C from T where A in (select A from T where B = 0);
but isn't it possible to select all C values where column B contains a 0 and that match column A with those?
I'd gladly stand by if more information is needed since it is a quite fuzzy question, but SQL can be confusing sometimes.
Tough to tell without your example result set; but maybe something like this:
SELECT A, B, C
FROM myTable
WHERE (B = 0 AND C LIKE '%A%')
OR (B <> 0 AND B = A)
I think you just want an or condition:
select C
from MyTable
where b = 0 or A in (select A from T where B = 0)
Is this the version you want:
select C
from MyTable
where C = 'a' or A in (select A from T where B = 0)