SQL UPDATE Self-join with compound matching value - sql-server

I have a MSSQL table that contains rows, some of which I need to update, depending upon the absence/presence of other rows in the same table. the table and sample data look like this:
SQL Table:
TABLE tblConnector(
ID1 [int] NOT NULL,
ID2 [int] NOT NULL,
ID3 [int] NOT NULL,
...other cols...)
having PRIMARY KEY CLUSTERED comprised of ID1, ID2 and ID3 - meaning that the compound key must be unique.
Data Structure:
ID1 ID2 ID3
111 222 1
111 222 9999
333 444 1 <--- update only this row
555 666 1
555 666 9999
777 888 2
777 888 9999
123 456 3 <--- don't update this row
I need to update any row in this table, setting ID3 = 9999, where ID3 currently is 1 and there are no other rows in the table with the same values for ID1 and ID2, with ID3 = 9999.
In my sample data, I only want to update the 3rd row, setting ID3 = 9999 - because it has ID3 = 1 and there are no other rows in the table with the same ID1 and ID2 values (where ID3 <> 1). I don't want to update the last row - so using count() to isolte rows isn't a valid approach.
I've tried numerous attempts to join the table to itself, but I can't seem to get an UPDATE statement that will only affect the rows I want.
Any advice on how to code this SQL?
UPDATE: I should have been more clear - I want to update the ID3 column to 9999 ONLY if the current value in ID3 is 1 ... AND there are no other rows that have the same ID1 and ID2 values and ID3 = 9999. ID1, ID2 and ID3 comprise a unique key and setting 9999 in ID3 can't duplicate a key value already in existence.

Maybe I'm reading that differently.
;WITH cte AS (
SELECT ID1, ID2
FROM tblConnector
GROUP BY ID1, ID2
HAVING COUNT(1) = 1
)
UPDATE tblConnector
SET ID3 = 9999
FROM tblConnector a
INNER JOIN
cte b ON a.ID1 = b.ID1
AND a.ID2 = b.ID2
sqlfiddle
EDIT (after comment)
Added your new sample records and WHERE ID3 = 1 to the UPDATE. In this case 1 is a placeholder for whatever single value you want to plugin.
;WITH cte AS (
SELECT ID1, ID2
FROM tblConnector
GROUP BY ID1, ID2
HAVING COUNT(1) = 1
)
UPDATE tblConnector
SET ID3 = 9999
FROM tblConnector a
INNER JOIN
cte b ON a.ID1 = b.ID1
AND a.ID2 = b.ID2
WHERE ID3 = 1
New fiddle

Following your exact wording, the following should work:
UPDATE tblConnector t1
SET ID3 = 9999
WHERE NOT EXISTS
(
SELECT 1
FROM tblConnector t2
WHERE t2.ID1 = t1.ID1
AND t2.ID2 = t1.ID2
AND t2.ID3 <> 9999
)

Without EXISTS, without CTE approach. I don't know if this will be most efficient.
update t1
set t1.id3 = 9999
--select *
from
tblConnector as t1
left join
(
select id1, id2, COUNT(id1) as Rowz
from tblConnector as t2
group by id1, id2
) as src
on t1.id1 = src.id1
where Rowz = 1

Related

Exclude row based in multiple column value

i need to exclude rows based multiple column conditions like 1) first find value "rev" from column ID1, 2) find the corresponding value in column ID2 based on 1st condition i.e. Ref1, and exclude all the rows contains the value in the 2nd column.
ID1 ID2 VALUE
post Ref1 12
post Ref1 3
rev Ref1 12
rev Ref1 3
post Ref2 10
post Ref2 5
output
ID1 ID2 VALUE
post Ref2 10
post Ref2 5
This assumes that "rev" stands for reversal or something similar, and that if the ID2 and VALUE are the same for a "post" and a "rev" they negate each other.
I created your table with this:
DROP TABLE IF EXISTS #table1;
CREATE TABLE #Table1 (ID1 VARCHAR(4), ID2 VARCHAR(4), VALUE INT);
INSERT INTO #Table1 (ID1, ID2, VALUE)
VALUES ('post', 'Ref1', 12),
('post', 'Ref1', 3),
('rev', 'Ref1', 12),
('rev', 'Ref1', 3),
('post', 'Ref2', 10),
('post', 'Ref2', 5);
I've then written this query to initially fetch all the 'post' rows of data, then self join matching on ID2 and VALUE and that ID1 now has to be 'rev', then in the WHERE clause adding something to only return rows where there is no T2.ID1
SELECT T1.ID1,
T1.ID2,
T1.VALUE
FROM #Table1 AS T1
LEFT JOIN #Table1 AS T2 ON T2.ID2 = T1.ID2 AND T2.VALUE = T1.VALUE AND T2.ID1 = 'rev'
WHERE T1.ID1 = 'post' AND T2.ID1 IS NULL;
You can first select all rows having ID1 = 'post' then remove all records having ID1 = 'rev', when 'rev' is replaced with 'post'.
SELECT * FROM tab WHERE [ID1] = 'post'
EXCEPT
SELECT 'post' AS [ID1], [ID2], [VALUE] FROM tab WHERE [ID1] = 'rev'
Check the demo here.

Update(Rename) values of a column based on the values of another column in same table - T-SQL

I'm trying to rename a column based on another column in the same table. Example - I have a table as below
Row# Name Date id
-------------------------------------
1 aaa 2018-03-02 Null
2 aaa 2018-03-02 123
3 aaa 2018-03-02 456
4 bbb 2019-07-05 Null
5 bbb 2019-07-05 Null
6 bbb 2019-07-05 345
Here, I would like to check if the name and sent date match - if both the condition match and the Id is NULL, no changes in the name but if the id is not NULL then I want to rename the 'Name' field as 'aaa (temp)' in the same table.
I'm not sure how to compare the id field and rename the table if its not NULL (irrespective of what the value is) below is the solution I'm expecting (Since the name & Date are same but the Ids in row 2 & 3 are not NULL)
Row# Name Date id
-------------------------------------------
1 aaa 2018-03-02 Null
2 aaa(Temp) 2018-03-02 123
3 aaa(Temp) 2018-03-02 456
4 bbb 2019-07-05 Null
5 bbb 2019-07-05 Null
6 bbb(Temp) 2019-07-05 345
You can made a update with inner join
UPDATE e1 SET name = CONCAT(e1.name,' ( Temp )')
FROM #example e1
INNER JOIn #example e2
ON e2.name = e1.name
AND e2.date = e1.date
WHERE e1.id IS NOT NULL
But because you dont have a unique identifier for each row is going to also update the row that dont have a match, bc each rows is going to match with itself.
If you have uid is pretty easy
UPDATE e1 SET name = CONCAT(e1.name,' ( Temp )')
FROM #example e1
INNER JOIN #example e2
ON e2.name = e1.name
AND e2.date = e1.date
AND e2.id_row <> e1.id_row
WHERE e1.id is not null
if you don't really have a unique identifier you can easily add it with this query
ALTER TABLE #example ADD id_row INT IDENTITY NOT NULL
You can use exists and case:
select t.*,
(case when id is not null and
exists (select 1 from t t2 where t2.name = t.name and t2.date = t.date and t2.id is null)
then concat(name, '(Temp)')
else name
end) as new_name
from t;
If you want to update the value, then you can use an updatable CTE:
with toupdate as (
select t.*,
(case when id is not null and
exists (select 1 from t t2 where t2.name = t.name and t2.date = t.date and t2.id is null)
then concat(name, '(Temp)')
else name
end) as new_name
from t
)
update toudpate
set name = new_name
where name <> new_name;
You can also phrase this as:
update t
set name = concat(name, '(Temp)')
where t.id is not null and
exists (select 1 from t t2 where t2.name = t.name and t2.date = t.date and t2.id is null);

SQL Server - Generate the same random number into multiple columns

I would like to generate a random number (5 or 6 digits) but not just into one column, in 4 actually.
ID1 ID2 Type LotNumber ID3 ID4
-------------------------------------------------------------
721 721 1 Lot1 721 721
721 721 1 Lot1 721 721
Looking over the internet and several articles, mostly generate a random number to be used in just one column and that's it. Is there any way I can achieve this using SQL? Thanks
EDIT
I used this statement:
SELECT FLOOR(RAND(CHECKSUM(NEWID()))*(99999-10000+1)+1000) AS ID
VIEW
CREATE VIEW MyView
AS
SELECT
'' AS ID1,
'' AS ID2,
CASE WHEN t.fADTableField4Code='Aggregated' THEN CAST(1 AS BIT) ELSE CAST(2 AS BIT) END AS Type,
ISNULL(t.fADTableField3Code, '') AS LotNumber,
'' AS ID3,
'' AS ID4
FROM MyTable AS t
GO
XML File
The XML file has to look like this:
<Documents>
<Document>
<ID1>721</ID1>
<ID2>721</ID2>
<Type>1</Type>
<LotNumber>Lot1</LotNumber>
<ID3>721</ID3>
<Date>2018-12-04</Date>
<Details>
<Detail>
<ID4>721</ID4>
............
</Details>
</Document>
Try this and expand upon it.
select a.*
, b.id as ID1, b.id as ID2 --Get ID from applied table and use as often as you need
from (select 1) as a(ID) -- this is your main table
cross apply (SELECT FLOOR(RAND(CHECKSUM(NEWID()))*(99999-10000+1)+1000) AS ID) B
you can use either a CTE or a subquery
SELECT
ID1 = R.ID
,ID2 = R.ID
,ID3 = R.ID
,ID4 = R.ID
FROM
(SELECT
FLOOR(RAND(CHECKSUM(NEWID()))*(99999-10000+1)+1000)
AS ID
) R
;WITH R
AS
(SELECT
FLOOR(RAND(CHECKSUM(NEWID()))*(99999-10000+1)+1000)
AS ID
)
SELECT
ID1 = R.ID
,ID2 = R.ID
,ID3 = R.ID
,ID4 = R.ID
FROM R

Update a table using temp table with select statement for multiple rows

I have temp table in my SP and a table in my DB and I need to update the table in the DB, and as of now I am able to update the table using select statement.
But I am having multiple records in my temp table and I am able to update only my last row of the table in DB.
Below is the query I am having,
UPDATE
Table_A
SET
Table_A.col2 = Table_B.col2,
Table_A.col3 = Table_B.col3
FROM
Some_Table AS Table_A
INNER JOIN temp_Table AS Table_B ON Table_A.col1 = Table_B.col1
And DB table structures
col1 | col2 | col3
1 | India | Delhi
2 | US | NewYork
3 | UK | London
And temp table structure as below
col1 | col2 | col3
1 | US | NewYork
2 | UK | London
3 | India | Delhi
So, I need to update my table for multiple rows.
As far as I am understanding your post I think this should be the solution for that which will update properly. Hope this helps
WITH CTE1
AS ( SELECT Col1 ,
Col2 ,
ROW_NUMBER() OVER ( PARTITION BY COl1 ORDER BY Col2 ) AS rn
FROM table_1 t1
),
CTE2
AS ( SELECT Col1 ,
Col2 ,
ROW_NUMBER() OVER ( PARTITION BY COl1 ORDER BY Col2 ) AS rn
FROM table_2 t2
)
UPDATE br
SET ....
FROM Cte1 c1
INNER JOIN cte2 c2 br ON c1.Col1 = c2.Col1
AND c1.rn = c2.rn;

Join two different columns from two different tables and merge duplicates

i have two temporary table
Table 1
ID1 Name ID2 Single
----------------------------------------------------
1 ABC 1 100
2 DEF 1 200
Table 2
ID1 Name ID2 Monthly
----------------------------------------------------
3 PQR 2 500
4 LMN 2 600
1 ABC 2 700
2 DEF 2 800
I want Output
ID1 Name ID2 Single Monthly
--------------------------------------------------------
1 ABC 1 100 700
2 DEF 1 200 800
3 PQR 2 NULL 500
4 LMN 2 NULL 600
I used all Joins , Union ALL , Union nothing working
thanks in advance
Try this:
select coalesce(T1.ID1, T2.ID1) as ID1,
coalesce(T1.Name, T2.Name) as ID1,
coalesce(T1.ID2, T2.ID2) as ID2,
T1.Single,
T2.Monthly
from Table1 as T1
full outer join Table2 as T2
on T1.ID1 = T2.ID1
https://data.stackexchange.com/stackoverflow/q/121659/
If you know that all rows always will be present in Table2 you can use a right outer join instead of full join.
Hope you are using Sql Server 2008(other wise the insert statement in my query won't work). Try this one.
From the required out put, i guess you need all the values from table2 and there corresponding Single(Column name in table 1) value.
DECLARE #tempTable1 TABLE (ID1 INT,Name VARCHAR(10),ID2 INT,Single INT)
DECLARE #tempTable2 TABLE (ID1 INT,Name VARCHAR(10),ID2 INT,Monthly INT)
INSERT INTO #tempTable1 VALUES
(1 ,'ABC' ,1 ,100),
(2 ,'DEF' ,1 ,200)
INSERT INTO #tempTable2 VALUES
(3 ,'PQR' ,2 ,500 ),
(4 ,'LMN' ,2 ,600 ),
(1 ,'ABC' ,2 ,700 ),
(2 ,'DEF' ,2 ,800 );
SELECT
T2.ID1
,T2.Name
,T2.ID2
,T1.Single
,T2.Monthly
FROM #tempTable2 T2
LEFT OUTER JOIN #tempTable1 T1
ON T2.ID1 = T1.ID1
ORDER BY T2.ID1

Resources