UPDATE query using value as condition - sql-server

I have an update query which takes the form:
update myTable
set aColumn = case when t2.someValue = 3 then 1 else 0 end
from myTable
inner join anotherTable t2 on t2.id = myTable.id
Trouble is, it doesn't work.
The 'case' or t2.someValue seems to be being ignored, and every row in myTable gets the same value (0) in aColumn - including those rows where t2.someValue = 3.
Is this a problem with SQL Server, or have I done something wrong?
EDIT: My updated query, based on a comment below...
declare #myTable table (row_id int, aCol int, bCol int)
insert into #myTable
values
(1,0,0),
(2,0,0),
(3,0,0),
(4,0,0),
(5,0,0)
declare #anotherTable table (row_id int, col_id int, someValue int)
insert into #anotherTable
values
(1,1,9),
(1,2,8),
(1,3,7),
(4,3,6),
(5,5,5)
select * from #myTable
update t
set t.aCol = case when t2.col_id = 1 then t2.someValue else t.aCol end,
t.bCol = case when t2.col_id = 3 then t2.someValue else t.bCol end
from #myTable t
left join #anotherTable t2 on t2.row_id = t.row_id
where t2.col_id IN (1,2,3,5)
select * from #myTable
This doesn't update the bCol of the 1st row with a 7 which it should...
row_id aCol bCol
1 9 0
2 0 0
3 0 0
4 0 6
5 0 0

Try this -
update myTable
set aColumn = (case when t2.someValue = 3 then 1 else 0 end)
from myTable
inner join anotherTable t2 on t2.id = myTable.id

From your edit... you have a 1:Many relationship and this is why it's not happening. You have 3 rows in #anotherTable which can join to tod_id = 1 in #myTable. This is why your update isn't working correctly.
declare #myTable table (row_id int, aCol int, bCol int)
insert into #myTable
values
(1,0,0),
(2,0,0),
(3,0,0),
(4,0,0),
(5,0,0)
declare #anotherTable table (row_id int, col_id int, someValue int)
insert into #anotherTable
values
(1,1,9),
(1,2,8),
(1,3,7),
(4,3,6),
(5,5,5)
select
t.*
,t2.*
from #myTable t
left join #anotherTable t2 on t2.row_id = t.row_id
where t2.col_id IN (1,2,3,5)
+--------+------+------+--------+--------+-----------+
| row_id | aCol | bCol | row_id | col_id | someValue |
+--------+------+------+--------+--------+-----------+
| 1 | 0 | 0 | 1 | 1 | 9 | --notice the implied cross join as
| 1 | 0 | 0 | 1 | 2 | 8 | --row_id 1 is duplicated
| 1 | 0 | 0 | 1 | 3 | 7 | --3 times
| 4 | 0 | 0 | 4 | 3 | 6 |
| 5 | 0 | 0 | 5 | 5 | 5 |
+--------+------+------+--------+--------+-----------+

Ok, Given the advice above - esp. from SCSimon - I have to admit that what I was trying to do was unusual, and too much of an edge-case, for SQL (almost any flavour) to understand.
I have resorted to making the updates to the 12 individual columns as seperate little update statements.
Not as elegant, maybe, but it works without error.
I also doubt that it actually takes any longer to run than a complex query would.

Related

Extract numbers from string in sql

In my Table there is a column called Comment and it contains data like
input
Comment
| 22 | 22 | INTERNAL AUDIT | NM | OK
| Multiplied by 4 | 32 | 32 | INTERNAL AUDIT | TR | None
| 19 | 17 | INTERNAL AUDIT | LM | FIXED
| REF#R7F282CT
we need to extract only numbers from this comment column and update into other column
need output like this like:
col1 col2
22 22
32 32
19 17
null null
input image
Output image
Using Split Function
declare #temp1 table (Comment varchar(255))
insert into #temp1 values ('| 22 | 22 | INTERNAL AUDIT | NM | OK')
insert into #temp1 values ('| Multiplied by 4 | 32 | 32 | INTERNAL AUDIT | TR | None')
insert into #temp1 values ('| 19 | 17 | INTERNAL AUDIT | LM | FIXED')
insert into #temp1 values ('| REF#R7F282CT')
declare #temp2 table (Comment varchar(255),numeric_values varchar(100))
insert into #temp2
Select Comment,[value] as numeric_values
from #temp1
CROSS APPLY string_split(Comment,'|')
WHERE ISNUMERIC([value]) = 1
--select * from #temp2
SELECT Comment,
MAX(CASE WHEN Row_Num = 1 THEN numeric_values END) Col1,
MAX(CASE WHEN Row_Num = 2 THEN numeric_values END) Col2
FROM (
SELECT Comment,numeric_values,
ROW_NUMBER() OVER(PARTITION BY comment ORDER BY (select null)) as Row_Num
FROM #temp2
) d
GROUP BY Comment

SQL Server split field and create addition rows with values from main row

I have a table with rows and in one field there are values like this A,B,C
Table 'Mytable':
|ID | Date | MyValue | SplitID |
|1 | 2019-12-17 | A | |
|2 | 2019-12-15 | A,B | |
|3 | 2019-12-16 | B,C | |
Result should be:
|1 | 2019-12-17 | A | 1 |
|2 | 2019-12-15 | A | 2 |
|4 | 2019-12-15 | B | 2 |
|3 | 2019-12-16 | B | 3 |
|5 | 2019-12-16 | C | 3 |
(Sorry, I could not find HOW to format a table in the Stackoverflow help)
I tried a inline table function which splits the Field Myvalue into more lines but could not pass my rows with
charindex(',',[MyValue])>0
from MyTable as input lines.
The code is this:
ALTER function [dbo].[fncSplitString](#input Varchar(max), #Splitter Varchar(99), #ID int)
returns table as
Return
with tmp (DataItem, ix, ID) as
( select LTRIM(#input) , CHARINDEX('',#Input), #ID --Recu. start, ignored val to get the types right
union all
select LTRIM(Substring(#input, ix+1,ix2-ix-1)), ix2, #ID
from (Select *, CHARINDEX(#Splitter,#Input+#Splitter,ix+1) ix2 from tmp) x where ix2<>0
) select DataItem,ID from tmp where ix<>0
Thanks for help
Michael
You can try the following query.
Create table #Temp(
Id int,
DateField Date,
MyValue Varchar(10),
SplitID int
)
CREATE FUNCTION [dbo].[SplitPra] (#Value VARCHAR(MAX), #delimiter CHAR)
RETURNS #DataResult TABLE([Position] TINYINT IDENTITY(1,1),[Value] NVARCHAR(128))
AS
BEGIN
DECLARE #XML xml = N'<r><![CDATA[' + REPLACE(#Value, #delimiter, ']]></r><r><![CDATA[') + ']]></r>'
INSERT INTO #DataResult ([Value])
SELECT RTRIM(LTRIM(T.c.value('.', 'NVARCHAR(128)')))
FROM #xml.nodes('//r') T(c)
RETURN
END
insert into #Temp Values(1, '2019-12-17', 'A', NULL),(2, '2019-12-15', 'A,B', NULL), (3, '2019-12-16', 'B,C', NULL)
Select
#Temp.Id, DateField, b.Value as MyValue, b.Id as SplitValue
from #Temp inner join (
select
Id, f.*
from
#Temp u
cross apply [dbo].[SplitPra](u.MyValue, ',') f
)b on #Temp.Id = b.Id
Drop table #Temp
This will give an output as shown below.
Id DateField MyValue SplitValue
---------------------------------
1 2019-12-17 A 1
2 2019-12-15 A 2
2 2019-12-15 B 2
3 2019-12-16 B 3
3 2019-12-16 C 3
You can find the live demo here.
I found this solution, i hope it will work for you. But i didn't use your function to solve this problem. Instead of that, i used cross apply function.You can find the query below:
-- Creating Test Table
CREATE TABLE #Test
(
ID int,
Date date,
MyValue nvarchar(max),
SplitID int
);
GO
-- Inserting data into test table
INSERT INTO #Test VALUES (1, '2019-12-17', 'A', NULL);
INSERT INTO #Test VALUES (2, '2019-12-15', 'A,B', NULL);
INSERT INTO #Test VALUES (3, '2019-12-16', 'B,C', NULL);
GO
-- Select query
SELECT
*,
(SELECT ID FROM test t1 WHERE t.Date = t1.date) AS SplitID
FROM
(
SELECT
ROW_NUMBER() OVER (ORDER BY ID) AS ID,
Date,
substring(A.value,1,
CASE WHEN charindex(',',rtrim(ltrim(A.value))) = 0 then LEN(A.value)
ELSE charindex(',',rtrim(ltrim(A.value))) -1 end) as MyValue
FROM Test
CROSS APPLY string_split (MyValue, ',') A) AS T
ORDER BY MyValue ASC;
And the result must be like that:
ID Date MyValue SplitID
1 2019-12-17 A 1
2 2019-12-15 A 2
3 2019-12-15 B 2
4 2019-12-16 B 3
5 2019-12-16 C 3

Get multiple rows from a subquery SQL

Basically I have 2 Tables, the first with the raw material amount (QT) for each serial number and the second one with how much raw material was spent (Qt_Added) on batch's production. Like this:
Table 1
+----------+------------+-----+
| Code_Raw | Serial_Raw | Qt |
+----------+------------+-----+
| 1 | 1 | 100 |
| 1 | 2 | 150 |
| 2 | 1 | 80 |
| 1 | 3 | 100 |
+----------+------------+-----+
And Table 2
+------------+----------+------------+----------+--+
| Code_Batch | Code_Raw | Serial_Raw | Qt_Added | |
+------------+----------+------------+----------+--+
| 1 | 1 | 1 | 80 | |
| 2 | 1 | 1 | 10 | |
| 3 | 1 | 2 | 150 | |
| 4 | 1 | 3 | 80 | |
+------------+----------+------------+----------+--+
I tried to do a query for a specific Code_Raw, show me how much left for each serial number, But worked only when there's a single serial_raw.
My query:
select *
from
(select
Serial_Raw,
(Select QT From Table_1 where Code_Raw = 1) - Sum(qt_added) as Total_Remaining
from
Table_2
where
Cod_Raw = 1
group by
Serial_Raw) e
where
Total_Remaining > 0
but it throws this error
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression
And I expect :
Serial_Raw Total_Remaining
-------------------------------
1 10
3 20
Is there a struct problem or another way to do this?
I'm using SQL Server 2014
Thanks guys
Try this:
DECLARE #tbl1 TABLE
( CodeRaw INT,
Serial_Raw INT,
Qty INT)
DECLARE #tbl2 TABLE
(
CodeBatch INT,
CodeRaw INT,
Serial_Raw INT,
QtyAdded INT)
INSERT INTO #tbl1 VALUES(1,1,100)
INSERT INTO #tbl1 VALUES(1,2,150)
INSERT INTO #tbl1 VALUES(2,1,80)
INSERT INTO #tbl1 VALUES(1,3,100)
INSERT INTO #tbl2 VALUES(1,1,1,80)
INSERT INTO #tbl2 VALUES(2,1,1,10)
INSERT INTO #tbl2 VALUES(3,1,2,150)
INSERT INTO #tbl2 VALUES(4,1,3,80)
--Inner table has the summary of the Quantity added with columns CodeRaw and SerialRaw. Outer table make join with inner table and just substruct with the Qty and Sum of Qty Added.
SELECT t2.Serial_Raw, t1.Qty - t2.QtyAdded AS Total_Remaining FROM #tbl1 t1
INNER JOIN (SELECT CodeRaw, Serial_Raw , SUM(QtyAdded) QtyAdded FROM #tbl2
GROUP BY CodeRaw, Serial_Raw) AS t2 ON t2.CodeRaw = t1.CodeRaw AND t1.Serial_Raw = t2.Serial_Raw
WHERE t1.Qty - t2.QtyAdded > 0
If I understood you right, this might be what you are after
declare #tbl1 table (CodeRaw INT, Serial_Raw INT, Qty INT)
declare #tbl2 table (CodeBatch INT, CodeRaw INT, Serial_Raw INT, QtyAdded INT)
insert into #tbl1 values (1,1,100), (1,2,150), (2,1,80), (1,3,100)
insert into #tbl2 values (1,1,1,80), (2,1,1,10), (3,1,2,150), (4,1,3,80)
select t2.Serial_Raw,
t3.Qty - sum(t2.QtyAdded) as Total_Remaining
from #tbl2 t2
inner join ( select t1.Serial_Raw,
t1.CodeRaw,
sum(t1.Qty) as Qty
from #tbl1 t1
group by t1.Serial_Raw, t1.CodeRaw
) t3
on t2.Serial_Raw = t3.Serial_Raw
and t2.CodeRaw = t3.CodeRaw
group by t2.Serial_Raw, t3.Qty
So in t2 we get all distinct Serial_Raw values, and sum their QtyAdded from the first table.
In t3 we get all Qty values from the 2nd table.
All we need to do is join them together and subtract
The result of this query is
Serial_Raw Total_Remaining
---------- ---------------
1 10
2 0
3 20

Update each row of the table with data from table itself

I have a problem with updating work table - values from that table come from table itself. Here is my table:
+----------+----------+-----+---------+
| EVEN_KEY | INVE_KEY | QUA | QUA_MAX |
+----------+----------+-----+---------+
| 1 | 2 | 1 | NULL |
| 2 | 2 | 2 | NULL |
| 3 | 2 | 3 | NULL |
| 1 | 1 | 2 | NULL |
| 4 | 2 | 3 | NULL |
+----------+----------+-----+---------+
What I would like to do is update column qua_max - summarize column qua for given inve_key for each row. So, results in above table should look like after update:
+----------+----------+-----+---------+
| EVEN_KEY | INVE_KEY | QUA | QUA_MAX |
+----------+----------+-----+---------+
| 1 | 2 | 1 | 9 |
| 2 | 2 | 2 | 9 |
| 3 | 2 | 3 | 9 |
| 1 | 1 | 2 | 1 |
| 4 | 2 | 3 | 9 |
+----------+----------+-----+---------+
And here is my problem - query from this example is giving me error, I can not even run it. What is wrong?
Error:
Query:
UPDATE #TEMP_FINAL
SET QUA_MAX = (SELECT SUM(QUA)
FROM #TEMP_FINAL t2
WHERE #TEMP_FINAL.INVE_KEY = t2.INVE_KEY
GROUP BY INVE_KEY
)
EXAMPLE TABLE:
DECLARE #TEMP_FINAL TABLE
(
EVEN_KEY INT,
INVE_KEY INT,
QUA INT,
QUA_MAX INT
)
insert into #TEMP_FINAL (even_key, inve_key, qua)
values(1, 2, 1),
(2,2,2),
(3,2,3),
(1,1,2),
(4,2,3)
You can try this..
UPDATE t1
SET t1.QUA_MAX = a.sum_qua
from #temp_final t1,
(SELECT SUM(QUA) as sum_qua,inve_key
FROM #TEMP_FINAL t2
GROUP BY INVE_KEY
) a
where t1.INVE_KEY = a.INVE_KEY
You are looking for window function
sum(QUA) over (partition by INVE_KEY)
Note that your sample data for #TEMP_FINAL has QUA = 2 for INVE_KEY = 1 which isn't what the original sample data was.
select
*,
QUA_MAX = sum(QUA) over (partition by INVE_KEY)
from #TEMP_FINAL
And one way to update it would be with a correlated subquery
update t1
set QUA_MAX = (select top 1 sum(QUA) over (partition by INVE_KEY) from #TEMP_FINAL t2 where t2.INVE_KEY = t1.INVE_KEY)
from #TEMP_FINAL t1
select * from #TEMP_FINAL
Or a CTE
;with cte as(
select
EVEN_KEY,
INVE_KEY,
QUA,
QUA_MAX = sum(QUA) over (partition by INVE_KEY)
from #TEMP_FINAL)
update #TEMP_FINAL
set QUA_MAX = c.QUA_MAX
from cte c
where c.INVE_KEY = [#TEMP_FINAL].INVE_KEY
select * from #TEMP_FINAL
i think that you should add temp_final in the from clause, because you're doing an implicit selfjoin, but you declare just a table as alias t2
So here is your complete code:
DECLARE #TEMP_FINAL TABLE
(
EVEN_KEY INT,
INVE_KEY INT,
QUA INT,
QUA_MAX INT
)
insert into #TEMP_FINAL (even_key, inve_key, qua)
values(1, 2, 1),
(2,2,2),
(3,2,3),
(1,1,2),
(4,2,3)
UPDATE #TEMP_FINAL
SET QUA_MAX = (SELECT SUM(QUA)
FROM #TEMP_FINAL t2
WHERE #TEMP_FINAL.INVE_KEY = t2.INVE_KEY
GROUP BY INVE_KEY
)
I don't know if I am looking at this wrong but to my it looks like you are doing a where clause comparing that same thing to itself? But instead of doing #TEMP_FINAL.INVE_KEY = #TEMP_FINAL.INVE_KEY you're doing #TEMP_FINAL.INVE_KEY = t2.INVE_KEY. Sorry for bringing this up I just wanted to verify that this was intentional or not?

SQL JOIN with COALESCE in condition is duplicating rows

I am trying to join two tables together. The first table contains data records that I do not want to duplicate. The second table I am joining to the first table to lookup a [value] by a distinct [profileId] and [role]. The [profileId], [role] column in the second table has a unique constraint on the combination, but [role] can sometimes be NULL, in which case I treat that value as the default for that profile.
How can I join these tables together without duplicating the rows, and without using multiple left joins? My actual query is more complex than the example.
See example below.
DECLARE #temp TABLE ([profileId] int, [role] int)
DECLARE #temp2 TABLE ([profileId] int, [role] int, [value] nvarchar(50))
INSERT INTO #temp ([profileId], [role]) VALUES (1, 1)
INSERT INTO #temp ([profileId], [role]) VALUES (1, 2)
INSERT INTO #temp ([profileId], [role]) VALUES (2, 1)
INSERT INTO #temp ([profileId], [role]) VALUES (2, 2)
INSERT INTO #temp2 ([profileId], [role], [value]) VALUES (1, 1, 'MATCH')
INSERT INTO #temp2 ([profileId], [role], [value]) VALUES (1, NULL, 'DEFAULT1')
INSERT INTO #temp2 ([profileId], [role], [value]) VALUES (2, NULL, 'DEFAULT2')
SELECT
T1.[profileId],
T1.[role],
T2.value
FROM
#temp T1
JOIN #temp2 T2 ON T1.profileId = T2.profileId AND COALESCE(T2.[role], T1.[role]) = T1.[role]
This gives me (and I understand why)
================================
| profileId | role | value |
================================
| 1 | 1 | MATCH |
--------------------------------
| 1 | 1 | DEFAULT1 |
--------------------------------
| 1 | 2 | DEFAULT1 |
--------------------------------
| 2 | 1 | DEFAULT2 |
--------------------------------
| 2 | 2 | DEFAULT2 |
================================
While I want
================================
| profileId | role | value |
================================
| 1 | 1 | MATCH |
--------------------------------
| 1 | 2 | DEFAULT1 |
--------------------------------
| 2 | 1 | DEFAULT2 |
--------------------------------
| 2 | 2 | DEFAULT2 |
================================
This SQL works fine:
SELECT
T1.[role],
Value = coalesce(max(nullif(T2.value,'DEFAULT')),'DEFAULT')
FROM
#temp T1
JOIN #temp2 T2 ON COALESCE(T2.[role], T1.[role]) = T1.[role]
group by
T1.[role]
;
You can use APPLY and TOP:
SELECT
t.profileId,
t.role,
x.value
FROM #temp t
OUTER APPLY(
SELECT TOP 1 value
FROM #temp2
WHERE
profileId = t.profileId
AND (role = t.role OR role IS NULL)
ORDER BY
CASE WHEN role IS NOT NULL THEN 0 ELSE 1 END
)x
if you know the DEFAULT value use left join.
SQL Fiddle Demo
SELECT
T1.[role],
COALESCE(T2.value, 'DEFAULT') as value
FROM
temp T1
LEFT JOIN temp2 T2
ON T1.[role] = T2.[role];
Otherwise
SELECT
T1.[role],
COALESCE(T2.value, (SELECT value
FROM temp2
WHERE role is NULL and temp2.profileID = T1.profileID)) as value
FROM
temp T1
LEFT JOIN temp2 T2
ON T1.[role] = T2.[role]
AND T1.[profileID] = T2.[profileID]
;

Resources