Swap row values in SQL Server - sql-server

I have question about SQL Server
Source: emp
id | name | check |deptname
100 | a | 1 |ceo
100 | b | 2 |hr
100 | c | 3 |po
100 | d | 5 |no
101 | a | 1 |pm
101 | b | 5 |ceo
102 | a | 1 |rn
102 | b | 2 |han
Here same id have check 2 and 5 values then we need to replace check values to 2 check values for that id.
Based on above table I want load/output data into target table like below
Target : emp1
id | name | check |deptname
100 | a | 1 |ceo
100 | d | 2 |hr
100 | c | 3 |po
101 | a | 1 |pm
101 | b | 5 |ceo
102 | a | 1 |rn
102 | b | 2 |han
and I tried like below
select
a1.id,
a1.name,
isnull(a2.[check],a1.[check]) as [check]
from
emp as a1
left outer join
emp as a2 on a2.id = a1.id
and a1.[check] in (2,5)
and a2.[check] in (2,5)
and a2.[check] <> a1.[check]
where
a2.id is null
or (a1.[check] = 5
and a2.[check] = 2)
That query does not return the right result.
Please tell me how to write query to get the expected output in SQL Server

This is how you can do that:
select
e1.id,
isnull(e2.name, e1.name) as name,
e1.[check],
e1.deptname
from emp e1
left outer join emp e2
on e2.id = e1.id and e1.[check] = 2 and e2.[check] = 5
where
not exists (select 1 from emp e3 where e3.id = e1.id and
e1.[check] = 5 and e3.[check] = 2)
Example in SQL Fiddle

Related

How to update rows based on one field/column of snowflake tables

I want to update one table using another table on field "Id" such that it wont create duplicates
let say my first table is Table1 and second table is Table2 . I would like to update the row in Table1 from Table2 when the Id is matching
I am aware of using UNION function but this applies to entire columns where I only need to consider a single column.
https://docs.snowflake.com/en/sql-reference/operators-query.html#union-all
Example of my Tables
Table1
Id name number value
1 a 8 100
2 b 8 100
3 c 8 100
4 d 8 100
Table2
Id name number value
3 c 8 99
4 d 6 100
5 e 7 100
Expected output
Id name number value
1 a 8 100
2 b 8 100
3 c 8 99
4 d 6 100
5 e 7 100
Please note that in the output table row with Id 3,4 has be updated and new Id 5 is inserted. Can someone help me with the select query to get the desired output?
You can use MERGE command:
merge into table1 using table2 on table1.id = table2.id
when matched then
update set table1.name = table2.name, table1.number = table2.number, table1.value = table2.value
when not matched then
insert (Id,name,number,value) values (table2.id, table2.name, table2.number, table2.value);
select * from table1;
+----+------+--------+-------+
| ID | NAME | NUMBER | VALUE |
+----+------+--------+-------+
| 5 | e | 7 | 100 |
| 1 | a | 8 | 100 |
| 2 | b | 8 | 100 |
| 3 | c | 8 | 99 |
| 4 | d | 6 | 100 |
+----+------+--------+-------+
https://docs.snowflake.com/en/sql-reference/sql/merge.html
If you don't want to update the table, you may use IFNULL and full outer join:
select
IFNULL( t1.id, t2.id) id,
IFNULL( t2.name, t1.name ) name,
IFNULL( t2.number, t1.number ) number,
IFNULL( t2.value, t1.value ) value
from table1 t1
full join table2 t2
on t1.id = t2.id;
+----+------+--------+-------+
| ID | NAME | NUMBER | VALUE |
+----+------+--------+-------+
| 1 | a | 8 | 100 |
| 2 | b | 8 | 100 |
| 3 | c | 8 | 99 |
| 4 | d | 6 | 100 |
| 5 | e | 7 | 100 |
+----+------+--------+-------+

Create rows for missing values in SQL (data preparation for decision tree)

Currently, my table looks like this
id | valName | valCount | type
123 | abb | 3 | 2
123 | abc | 2 | 2
123 | b | 5 | 2
251 | aaa | 2 | 1
251 | ab | 2 | 1
251 | abb | 2 | 1
251 | ac | 2 | 1
and so on.
I want to fill in missing valNames for every id and set valCount to 0. If my set of distinct valName was (aaa, aab, ab, abb, abc, ac, b) it would look like this.
id | valName | valCount | type
123 | aaa | 0 | 2
123 | aab | 0 | 2
123 | ab | 0 | 2
123 | abb | 3 | 2
123 | abc | 2 | 2
123 | ac | 0 | 2
123 | b | 5 | 2
251 | aaa | 2 | 1
251 | aab | 0 | 1
251 | ab | 2 | 1
251 | abb | 2 | 1
251 | abc | 0 | 1
251 | ac | 2 | 1
251 | b | 0 | 1
Also, the dataset is quite large. So efficient query is better.
As Dale suggested, this is my attempt. TABLE in the code is the table I am using.
select C.id, C.valName, C.type, COALESCE(D.valCount,0 ) as count
from (
select *
from (select id, min(type) as type
From TABLE
Group by id
) B
cross join
(select distinct valName FROM TABLE) A
) C
left join TABLE D
on C.id = D.id
and C.valName = D.valName
order by C.id
The idea behind this query is to create the id/valname table using cross join and then get valCount using left join.
This query works but is too slow.
Something like this
with unq_id_type_cte(id, [type]) as (
select distinct id, [type] from mytable)
insert mytable(id, valName, valCount, [type])
select uitc.id, t.v, 0, uitc.[type]
from
(values ('aaa'),('aab'),('ab'),('abb'),('abc'),('ac'),('b')) t(v)
cross join
unq_id_type_cte uitc
where not exists
(select 1 from mytable t_in where uitc.id=t_in.id
and t.v=t_in.valName);
If there are performance issues or concerns then the first thing to try imo would be to insert the cte into an indexed temp table.

Getting duplicates with additional information

I've inherited a database and I'm having trouble constructing a working SQL query.
Suppose this is the data:
[Products]
| Id | DisplayId | Version | Company | Description |
|---- |----------- |---------- |-----------| ----------- |
| 1 | 12345 | 0 | 16 | Random |
| 2 | 12345 | 0 | 2 | Random 2 |
| 3 | AB123 | 0 | 1 | Random 3 |
| 4 | 12345 | 1 | 16 | Random 4 |
| 5 | 12345 | 1 | 2 | Random 5 |
| 6 | AB123 | 0 | 5 | Random 6 |
| 7 | 12345 | 2 | 16 | Random 7 |
| 8 | XX45 | 0 | 5 | Random 8 |
| 9 | XX45 | 0 | 7 | Random 9 |
| 10 | XX45 | 1 | 5 | Random 10 |
| 11 | XX45 | 1 | 7 | Random 11 |
[Companies]
| Id | Code |
|---- |-----------|
| 1 | 'ABC' |
| 2 | '456' |
| 5 | 'XYZ' |
| 7 | 'XYZ' |
| 16 | '456' |
The Versioncolumn is a version number. Higher numbers indicate more recent versions.
The Company column is a foreign key referencing the Companies table on the Id column.
There's another table called ProductData with a ProductId column referencing Products.Id.
Now I need to find duplicates based on the DisplayId and the corresponding Companies.Code. The ProductData table should be joined to show a title (ProductData.Title), and only the most recent ones should be included in the results. So the expected results are:
| Id | DisplayId | Version | Company | Description | ProductData.Title |
|---- |----------- |---------- |-----------|------------- |------------------ |
| 5 | 12345 | 1 | 2 | Random 2 | Title 2 |
| 7 | 12345 | 2 | 16 | Random 7 | Title 7 |
| 10 | XX45 | 1 | 5 | Random 10 | Title 10 |
| 11 | XX45 | 1 | 7 | Random 11 | Title 11 |
because XX45 has 2 "entries": one with Company 5 and one with Company 7, but both companies share the same code.
because 12345 has 2 "entries": one with Company 2 and one with Company 16, but both companies share the same code. Note that the most recent version of both differs (version 2 for company 16's entry and version 1 for company 2's entry)
ABC123 should not be included as its 2 entries have different company codes.
I'm eager to learn your insights...
Based on your sample data, you just need to JOIN the tables:
SELECT
p.Id, p.DisplayId, p.Version, p.Company, d.Title
FROM Products AS p
INNER JOIN Companies AS c ON p.Company = c.Id
INNER JOIN ProductData AS d ON d.ProductId = p.Id;
But if you want the latest one, you can use the ROW_NUMBER():
WITH CTE
AS
(
SELECT
p.Id, p.DisplayId, p.Version, p.Company, d.Title,
ROW_NUMBER() OVER(PARTITION BY p.DisplayId,p.Company ORDER BY p.Id DESC) AS RN
FROM Products AS p
INNER JOIN Companies AS c ON p.Company = c.Id
INNER JOIN ProductData AS d ON d.ProductId = p.Id
)
SELECT *
FROM CTE
WHERE RN = 1;
sample fiddle
| Id | DisplayId | Version | Company | Title |
|----|-----------|---------|---------|----------|
| 5 | 12345 | 1 | 2 | Title 5 |
| 7 | 12345 | 2 | 16 | Title 7 |
| 10 | XX45 | 1 | 5 | Title 10 |
| 11 | XX45 | 1 | 7 | Title 11 |
If i understood you correctly, you can use CTE to find all the duplicated rows from your table, then you can just use SELECT from CTE and even add more manipulations.
WITH CTE AS(
SELECT Id,DisplayId,Version,Company,Description,ProductData.Title
RN = ROW_NUMBER()OVER(PARTITION BY DisplayId, Company ORDER BY p.Id DESC)
FROM dbo.YourTable1
)
SELECT *
FROM CTE
Try this:
SELECT b.ID,displayid,version,company,productdata.title
FROM
(select A.ID,a.displayid,version,a.company,rn,a.code, COUNT(displayid) over (partition by displayid,code) cnt from
(select Prod.ID,displayid,version,company,Companies.code, Row_number() over (partition by displayid,company order by version desc) rn
from Prod inner join Companies on Prod.Company = Companies.id) a
where a.rn=1) b inner join productdata on b.id = productdata.id where cnt =2
You have to first get the current version and then you see how many times the DisplayID + Code show-up. Then based on that you can select only the ones that have a count greater than one. You can then INNER JOIN ProductData on the final query to get the Title.
WITH
MaxVersion AS --Get the current versions
(
SELECT
MAX(Version) AS Version,
DisplayID,
Company
FROM
#TmpProducts
GROUP BY
DisplayID,
Company
)
,CTE AS
(
SELECT
p.DisplayID,
c.Code,
COUNT(*) AS RowCounter
FROM
#TmpProducts p
INNER JOIN
#TmpCompanies c
ON
c.ID = p.Company
INNER JOIN
MaxVersion mv
ON
mv.DisplayID = p.DisplayID
AND mv.Version = p.Version
AND mv.Company = p.Company
GROUP BY
p.DisplayID,
c.Code
)
SELECT
p.*
FROM
#TmpProducts p
INNER JOIN
CTE c
ON
c.DisplayID = p.DisplayID
INNER JOIN
MaxVersion mv
ON
mv.DisplayID = p.DisplayID
AND mv.Company = p.Company
AND mv.Version = p.Version
WHERE
c.RowCounter > 1

SQL Server - Need to create multiple rows based on lookup value

How do I create multiple rows in a new table based on a common value in another table?
ProviderTable: PersonTable:
-------------------- ---------------------
ProviderID | GroupID PersonID | ProviderID
1 | A 100 | 1
2 | A 101 | 3
3 | A 102 | 8
4 | NULL 103 | 10
5 | B 104 | 5
6 | C 105 | 4
7 | B
8 | NULL
9 | NULL
10 | C
ProviderTable.ProviderID = PersonTable.ProviderID
I need to create a new table with a person row for each provider where Provider.GroupID=Provider.GroupID
Results I am looking for:
New-table:
PersonID | ProviderID
100 | 1
100 | 2
100 | 3
101 | 3
101 | 1
101 | 2
102 | 8
103 | 10
103 | 6
104 | 5
104 | 7
105 | 4
This quick version gets the sort order you are looking for. Test data is included:
DECLARE #pt table (
ProviderId int,
GroupId varchar(2)
)
DECLARE #pet table (
PersonId int,
ProviderId int
)
INSERT INTO #pt Values
(1,'A'),
(2,'A'),
(3,'A'),
(4,NULL),
(5,'B'),
(6,'C'),
(7,'B'),
(8,NULL),
(9,NULL),
(10,'C')
INSERT INTO #pet VALUES
(100,1),
(101,3),
(102,8),
(103,10),
(104,5),
(105,4)
SELECT pe.PersonId,
IsNull(p2.ProviderId, p1.providerId) As ProviderId
FROM #pt p1
INNER JOIN #pet pe
ON p1.ProviderId = pe.ProviderId
LEFT JOIN #pt p2
ON p1.GroupId = p2.GroupId
ORDER BY pe.personId,
CASE
WHEN pe.ProviderId = p2.ProviderId
Then 0
ELSE 1
END
You can use the following SQL-statement:
SELECT PER.PersonID, COALESCE(PG.ProviderID, PP.ProviderID) AS ProviederID
FROM PersonTable PER
INNER JOIN ProviderTable PP
ON PP.ProviderID = PER.ProviderID
LEFT OUTER JOIN ProviderTable PG
ON PG.GroupID = PP.GroupID;

Sql server join by group?

I have this table :
id | type | date
1 | a | 01/1/2012
2 | b | 01/1/2012
3 | b | 01/2/2012
4 | b | 01/3/2012
5 | a | 01/5/2012
6 | b | 01/5/2012
7 | b | 01/9/2012
8 | a | 01/10/2012
The POV is per date. if 2 rows contains the same date , so both will visible in the same line ( left join).
Same date can be shared by 2 rows max.
so this situation can't be :
1 | a | 01/1/2012
2 | b | 01/1/2012
3 | a | 01/1/2012
if in the same date there is group a and b show both of them in single line using left join
if in date there is only a group , show it as single line ( +null at the right side )
if in date there is only b group , show it as single line ( +null at the left side )
Desired result :
Date |typeA|typeB |a'id|b'id
01/1/2012 | a | b | 1 | 2
01/2/2012 | | b | | 3
01/3/2012 | | b | | 4
01/5/2012 | a | b | 5 | 6
01/9/2012 | | b | | 7
01/10/2012 | a | | 8 |
I know this suppose to be simple , but the main anchor of join here is the date.
The problem I've encountered is when I read line 1 , i search in the table all rows with the same date...fine. - its ok.
But when I read the second line , I do it also , and it yields the first row - which already was counted...
any help ?
here is the sql fiddle :
https://data.stackexchange.com/stackoverflow/query/edit/82605
I think you want a pivot
select
[date],
case when [a] IS null then null else 'a' end typea,
case when [b] IS null then null else 'b' end typeb,
a as aid,
b as bid
from yourtable src
pivot (max(id) for type in ([a],[b]))p
If you want to do it with joins..
select ISNULL(a.date, b.date), a.type,b.type, a.id,b.id
from
(select * from yourtable where type='a') a
full outer join
(select * from yourtable where type='b') b
on a.date = b.date

Resources