Interchange value of 2 row in sql table - sql

I have a table which has column name course. In 2nd row course is "C++" and in 4th row course is "ASP.net".
I want to interchange that to value with update query. How can I achieve this?

You can change the values with update, like:
update YourTable set Course = 'ASP.NET' where id = 2
update YourTable set Course = 'C++' where id = 4
or:
update YourTable
set Course =
case id
when 2 then 'ASP.NET'
when 4 then 'C++'
end
where id in (2,4)

Test table and data
create table YourTable(id int primary key, course varchar(10))
insert into YourTable values (1, 'Delphi')
insert into YourTable values (2, 'C++')
insert into YourTable values (3, 'Clipper')
insert into YourTable values (4, 'ASP.net')
Update to switch 2 and 4
update YourTable set
course = case id
when 4 then (select course from YourTable where id = 2)
when 2 then (select course from YourTable where id = 4)
else T.course
end
from
YourTable as T
where T.id in (2, 4)
Result
id course
1 Delphi
2 ASP.net
3 Clipper
4 C++

Related

Postgresql update column based on set of values from another table

Dummy data to illustrate my problem:
create table table1 (category_id int,unit varchar,is_valid bool);
insert into table1 (category_id, unit, is_valid)
VALUES (1, 'a', true), (2, 'z', true);
create table table2 (category_id int,unit varchar);
insert into table2 (category_id, unit)
values(1, 'a'),(1, 'b'),(1, 'c'),(2, 'd'),(2, 'e');
So the data looks like:
Table 1:
category_id
unit
is_valid
1
a
true
2
z
true
Table 2:
category_id
unit
1
a
1
b
1
c
2
d
2
e
I want to update the is_valid column in Table 1, if the category_id/unit combination from Table 1 doesn't match any of the rows in Table 2. For example, the first row in Table 1 is valid, since (1, a) is in Table 2. However, the second row in Table 1 is not valid, since (2, z) is not in Table 2.
How can I update the column using postgresql? I tried a few different where clauses of the form
UPDATE table1 SET is_valid = false WHERE...
but I cannot get a WHERE clause that works how I want.
You can just set the value of is_valid the the result of a ` where exists (select ...). See Demo.
update table1 t1
set is_valid = exists (select null
from table2 t2
where (t2.category_id, t2.unit) = (t1.category_id, t1.unit)
);
NOTES:
Advantage: Query correctly sets the is_valid column regardless of the current value and is a vary simple query.
Disadvantage: Query sets the value of is_valid for every row in the table; even thoes already correctly set.
You need to decide whether the disadvantage out ways the advantage. If so then the same basic technique in a much more complicated query:
with to_valid (category_id, unit, is_valid) as
(select category_id
, unit
, exists (select null
from table2 t2
where (t2.category_id, t2.unit) = (t1.category_id, t1.unit)
)
from table1 t1
)
update table1 tu
set is_valid = to_valid.is_valid
from to_valid
where (tu.category_id, tu.unit) = (to_valid.category_id, to_valid.unit)
and tu.is_valid is distinct from to_valid.is_valid;

how to remove duplicate records but need to keep their child table rows and tag them to survived row

can you please help me in writing the query for both table
database : sql server
master_table
primary name
1 a
2 a
3 a
4 b
5 b
6 c
7 c
foreign
key
reference
above table
1 aa
2 aaa
3 aaaa
4 bb
5 bbb
6 cc
7 ccc
expected output
now I need to remove duplicate from above table based upon name
after removing duplicates name
master_table
primary name
1 a
4 b
6 c
to remove duplicate records but need to keep their child table rows and tag them to survived row
foreign
key
reference
above table
fk name_city
1 aa
1 aaa
1 aaaa
4 bb
4 bbb
6 cc
6 ccc
can you please help me in writing the query for both table
database : sql server
Thanks Gordon Linoff for reply
Let me add more detail
how I think it can be done
added rownum to master_table based upon duplicated on name
primary name row_num
1 a 1
2 a 2
3 a 3
4 b 1
5 b 2
6 c 1
7 c 2
foreign
key
reference
above
table
fk name_city (map_name |get primarykey from above
based | table with joining condition
upon |map_name=name
matching |and rownum = 1)
fk
with
primary )
-----------------------------------------------------------------------
1 aa a 1
2 aaa a 1
3 aaaa a 1
4 bb b 4
5 bbb b 4
6 cc c 6
7 ccc c 6
Please suggest if this is the right way
Thanks a lot for your time and kind
CREATE TABLE #master (ID INT, Name VARCHAR(50)); --DROP TABLE #master
INSERT INTO #master VALUES (1, 'a')
INSERT INTO #master VALUES (2, 'a')
INSERT INTO #master VALUES (3, 'a')
INSERT INTO #master VALUES (4, 'b')
INSERT INTO #master VALUES (5, 'b')
INSERT INTO #master VALUES (6, 'c')
INSERT INTO #master VALUES (7, 'c')
-- create temporary mapping table
;WITH cte AS
(
SELECT ID, MIN(ID) OVER (PARTITION BY Name) AS [MinID]
FROM #master
)
SELECT *
INTO #TempMapping -- DROP TABLE #TempMapping
FROM cte
WHERE cte.ID <> cte.MinID;
-- check to make sure that the IDs mapped as expected
SELECT * FROM #TempMapping;
-- change FKed values to their respective MIN mappings
UPDATE nc
SET nc.fk = tmp.MinID
FROM name_city nc
INNER JOIN #TempMapping tmp
ON tmp.ID = nc.fk;
-- remove non-MIN IDs from master now that nothing references them
DELETE mstr
FROM #master mstr
INNER JOIN #TempMapping tmp
ON tmp.ID = mstr.ID;
If there are a lot of rows in the [name_city] table or concurrency issues (i.e. blocking), then the #TempMapping table should probably be a real table (e.g. "dbo.TempMasterMappings") instead of a temp table. At that point, you can do this one ID at a time in a loop to keep the transactions smaller and quicker. Just replace the UPDATE and DELETE queries above with the following (which can even be run from a stored procedure). This method will work for any number of millions of rows (assuming that there is an index on the [fk] field, which there should be anyway).
DECLARE #BatchSize INT; -- this can be an input param for a proc
SET #BatchSize = 5000;
DECLARE #CurrentIDtoChange INT,
#CurrentNewID INT;
BEGIN TRY
WHILE (1 = 1)
BEGIN
SELECT TOP (1)
#CurrentIDtoChange = map.ID,
#CurrentNewID = map.MinID
FROM dbo.TempMasterMappings map
ORDER BY map.ID ASC;
IF (##ROWCOUNT = 0)
BEGIN
DROP TABLE dbo.TempMasterMappings; -- clean up!
BREAK; -- exit outer loop
END;
WHILE (1 = 1)
BEGIN
UPDATE TOP (#BatchSize) nc
SET nc.fk = #CurrentNewID
FROM dbo.name_city nc
WHERE nc.fk = #CurrentIDtoChange
OPTION (OPTIMIZE FOR UNKNOWN);
IF (##ROWCOUNT = 0)
BEGIN
DELETE mstr -- clean up PK record
FROM dbo.[Master] mstr
WHERE mstr.ID = #CurrentIDtoChange;
DELETE tmm -- remove ID as it is fully migrated!
FROM dbo.TempMasterMappings tmm
WHERE tmm.ID = #CurrentIDtoChange;
BREAK; -- exit inner loop
END;
END;
END TRY
BEGIN CATCH
DECLARE #Message NVARCHAR(4000) = ERROR_MESSAGE();
RAISERROR(#Message, 16, 1);
END CATCH;
You need to replace all the ids in the second table with the minimum matching id in the first, if I understand correctly.
This query should return the result set you want:
select mt.minid, name_city
from (select t.*, min(id) over (partition by name) as minid
from master_table t
) mt join
table2 t2
on t2.id = t.id;
It is unclear from the question whether you just want to get the right output, or whether you want to modify the tables. Updating the tables would basically be changing the above select to a similar update query and then deleting the extra rows from the master table.

Sum the number of occurrence by id

Is it possible to COUNT the number of times a value occurs in a table, however, use the count of 1 if the value appears more than once for each id.
Take the below table as an example. We want to see if either {5,6} occurred for p_id. If more than 1 occurrence of {5,6} is found, treat it as 1. For eg. p_id 1, the total count is 1.
p_id status
1 5
1 6
1 2
2 5
2 5
3 4
3 2
4 6
4 2
4 5
..transforms to..
p_id count
1 1
2 1
3 0
4 1
COUNT(CASE status IN (5,6) THEN 1 END) does an overall count.
Use the CASE...WHEN... as follows:
SELECT a.id, ISNULL(b.cnt, 0)
FROM
(
SELECT DISTINCT id FROM tab
) a
LEFT JOIN
(
SELECT id, CASE COUNT(*) WHEN 1 THEN 0 ELSE 1 END 'cnt'
FROM tab WHERE val in (5, 6) GROUP BY id
) b
ON a.id = b.id
SQLFiddle
This solution provides a quick setup and a simple two-step explanation of how I do this, using your example. The second query provides the desired result:
CREATE TABLE #temp (p_id INT, [status] INT);
INSERT #temp VALUES (1,5);
INSERT #temp VALUES (1,6);
INSERT #temp VALUES (1,2);
INSERT #temp VALUES (2,5);
INSERT #temp VALUES (2,5);
INSERT #temp VALUES (3,4);
INSERT #temp VALUES (3,2);
INSERT #temp VALUES (4,6);
INSERT #temp VALUES (4,2);
INSERT #temp VALUES (4,5);
-- Simple two-step tutorial
-- First, group by p_id so that all p_id's will be shown
-- run this to see...
SELECT A.p_id
FROM #temp A
GROUP BY A.p_id;
-- Now expand your query
-- Next, for each p_id row found, perform sub-query to see if 1 or more exist with status=5 or 6
SELECT A.p_id
,CASE WHEN EXISTS(SELECT 1 FROM #temp B WHERE B.p_id=A.p_id AND [status] IN (5,6)) THEN 1 ELSE 0 END AS [Count]
FROM #temp A
GROUP BY A.p_id;
Use the SIGN() function. It is exactly what you are looking for.
SELECT
[p_id],
SIGN(COUNT(CASE WHEN [status] IN (5,6) THEN 1 END)) AS [count]
FROM #temp
GROUP BY p_id
You can translate 5,6 = 1 and rest to 0 then do max()
with cte as (
select p_id, case when status in (5,6) then 1 else 0 end status
from FROM #tem)
select p_id, max(status) status
from cte
group by p_id

Find rows with same ID and have a particular set of names

EDIT:
I have a table with 3 rows like so.
ID NAME REV
1 A 0
1 B 0
1 C 0
2 A 1
2 B 0
2 C 0
3 A 1
3 B 1
I want to find the ID wich has a particular set of Names and the REV is same
example:
Edit2: GBN's solution would have worked perfectly, but since i do not have the access to create new tables. The added constraint is that no new tables can be created.
if input = A,B then output is 3
if input = A ,B,C then output is 1 and not 1,2 since the rev level differs in 2.
The simplest way is to compare a COUNT per ID with the number of elements in your list:
SELECT
ID
FROM
MyTable
WHERE
NAME IN ('A', 'B', 'C')
GROUP BY
ID
HAVING
COUNT(*) = 3;
Note: ORDER BY isn't needed and goes after the HAVING if needed
Edit, with question update. In MySQL, it's easier to use a separate table for search terms
DROP TABLE IF EXISTS gbn;
CREATE TABLE gbn (ID INT, `name` VARCHAR(100), REV INT);
INSERT gbn VALUES (1, 'A', 0);
INSERT gbn VALUES (1, 'B', 0);
INSERT gbn VALUES (1, 'C', 0);
INSERT gbn VALUES (2, 'A', 1);
INSERT gbn VALUES (2, 'B', 0);
INSERT gbn VALUES (2, 'C', 0);
INSERT gbn VALUES (3, 'A', 0);
INSERT gbn VALUES (3, 'B', 0);
DROP TABLE IF EXISTS gbn1;
CREATE TABLE gbn1 ( `name` VARCHAR(100));
INSERT gbn1 VALUES ('A');
INSERT gbn1 VALUES ('B');
SELECT
gbn.ID
FROM
gbn
LEFT JOIN
gbn1 ON gbn.`name` = gbn1.`name`
GROUP BY
gbn.ID
HAVING
COUNT(*) = (SELECT COUNT(*) FROM gbn1)
AND MIN(gbn.REV) = MAX(gbn.REV);
INSERT gbn1 VALUES ('C');
SELECT
gbn.ID
FROM
gbn
LEFT JOIN
gbn1 ON gbn.`name` = gbn1.`name`
GROUP BY
gbn.ID
HAVING
COUNT(*) = (SELECT COUNT(*) FROM gbn1)
AND MIN(gbn.REV) = MAX(gbn.REV);
Edit 2, without extra table, use a derived (inline) table:
SELECT
gbn.ID
FROM
gbn
LEFT JOIN
(SELECT 'A' AS `name`
UNION ALL SELECT 'B'
UNION ALL SELECT 'C'
) gbn1 ON gbn.`name` = gbn1.`name`
GROUP BY
gbn.ID
HAVING
COUNT(*) = 3 -- matches number of elements in gbn1 derived table
AND MIN(gbn.REV) = MAX(gbn.REV);
Similar to gbn, but allowing for the possibility of duplicate ID/Name combinations:
SELECT ID
FROM MyTable
WHERE NAME IN ('A', 'B', 'C')
GROUP BY ID
HAVING COUNT(DISTINCT NAME) = 3;
OKAY!... I solved my problem ! I modified GBN's logic to do it without a search table using the IN clause
1 flaw with doing MAX(rev) = MIN(REV) is: if i have a data like so .
ID NAME REV
1 A 0
1 B 1
1 A 1
then when I use a query like
Select ID from TABLE
where NAME in {A,B}
groupby ID
having count(*) = 2
and MIN(REV) = MAX(REV)
it will not show me the ID 1 as the min and max are different and the count is 3.
So i simply add another column to the groupby
so the final query is
Select ID from TABLE
where NAME in {A,B}
groupby ID,REV
having count(*) = 2
and MIN(REV) = MAX(REV)
Thanks,to all that helped. !

Fixing duplicate rows in a table

I have a table like below
DECLARE #ProductTotals TABLE
(
id int,
value nvarchar(50)
)
which has following value
1, 'abc'
2, 'abc'
1, 'abc'
3, 'abc'
I want to update this table so that it has the following values
1, 'abc'
2, 'abc_1'
1, 'abc'
3, 'abc_2'
Could someone help me out with this
Use a cursor to move over the table and try to insert every row in a second temporary table. If you get a collision (technically with a select), you can run a second query to get the maximum number (if any) that's appended to your item.
Once you know what maximum number is used (use isnull to cover the case of the first duplicate) just run an update over your original table and keep going with your scan.
Are you looking to remove duplicates? or just change the values so they aren't duplicate?
to change the values use
update producttotals
set value = 'abc_1'
where id =2;
update producttotals
set value = 'abc_2'
where id =3;
to find duplicate rows do a
select id, value
from producttotals
group by id, value
having count() > 2;
Assuming SQL Server 2005 or greater
DECLARE #ProductTotals TABLE
(
id int,
value nvarchar(50)
)
INSERT INTO #ProductTotals
VALUES (1, 'abc'),
(2, 'abc'),
(1, 'abc'),
(3, 'abc')
;WITH CTE as
(SELECT
ROW_NUMBER() OVER (Partition by value order by id) rn,
id,
value
FROM
#ProductTotals),
new_values as (
SELECT
pt.id,
pt.value,
pt.value + '_' + CAST( ROW_NUMBER() OVER (partition by pt.value order by pt.id) as varchar) new_value
FROM
#ProductTotals pt
INNER JOIN CTE
ON pt.id = CTE.id
and pt.value = CTE.value
WHERE
pt.id NOT IN (SELECT id FROM CTE WHERE rn = 1)) --remove any with the lowest ID for the value
UPDATE
#ProductTotals
SET
pt.value = nv.new_value
FROM
#ProductTotals pt
inner join new_values nv
ON pt.id = nv.id and pt.value = nv.value
SELECT * FROM #ProductTotals
Will produce the following
id value
----------- --------------------------------------------------
1 abc
2 abc_1
1 abc
3 abc_2
Explanation of the SQL
The first CTE creates a row number Value. So the numbering gets restarted whenever it sees a new value
rn id value
-------------------- ----------- --------
1 1 abc
2 1 abc
3 2 abc
4 3 abc
The second CTE called new_values ignores any IDs that are assoicated with with a RN of 1. So rn 1 and rn 2 get removed because they share the same ID. It also uses ROW_NUMBER() again to determine the number for the new_value
id value new_value
----------- ------ -------------
2 abc abc_1
3 abc abc_2
The final statement just updates the Old value with the new value