How can I override rows from another table? - sql

I have two tables:
TableA
ID Name
-- ----
1 aaa
2 bbb
3 ccc
4 ddd
TableB
ID Name
-- --------
3 WWXXYYZZ
I want to select from both tables, but skip the rows which exist in TableB. The result should look like this:
ID Name
-- --------
1 aaa
2 bbb
3 WWXXYYZZ
4 ddd
I have tried union and join but did not figure out how to achieve this.
-- Did not work
select *
from TableA
union
select *
from TableB
-- Did not work
select *
from
(
select *
from TableA
) x
join
(
select *
from TableB
) y
on x.ID = y.ID

You could left join b on to a, and use coalesce to prefer b's rows:
SELECT a.id, COALESCE(b.name, a.name) AS name
FROM a
LEFT JOIN b ON a.id = b.id

You can do:
select a.id, coalesce(b.name, a.name)
from a left join b on a.id = b.id

One method is union all:
select b.*
from b
union all
select a.*
from a
where not exists (select 1 from a where a.id = b.id);
You can also choose from a and override with values from b:
select a.id, coalesce(b.name, a.name) as name
from a left join
b
on a.id = b.id;

A more complex method uses ROW_NUMBER which might be necessary if your query is significantly more complex than shown. It also handled the case where a row exists in TableB but not TableA (which is not clear from your question).
DECLARE #TableA TABLE (id INT, [Name] VARCHAR(12));
DECLARE #TableB TABLE (id INT, [Name] VARCHAR(12));
INSERT INTO #TableA (id, [Name])
VALUES
(1, 'aaa'),
(2, 'bbb'),
(3, 'ccc'),
(4, 'ddd');
INSERT INTO #TableB (id, [Name])
VALUES
(3, 'WWXXYYZZ'),
(5, 'TTTGGG');
SELECT id, [Name]
FROM (
SELECT id, [Name]
, ROW_NUMBER() OVER (PARTITION BY id ORDER BY [Priority] DESC) Rn
FROM (
SELECT id, [Name], 0 [Priority]
FROM #TableA
UNION ALL
SELECT id, [Name], 1 [Priority]
FROM #TableB
) X
) Y
WHERE Rn = 1;
Returns:
1 aaa
2 bbb
3 WWXXYYZZ
4 ddd
5 TTTGGG

Related

Case when duplicate add one more letter

For example: I have a table with these records below
1 A
2 A
3 B
4 C
...
and I need to migrate these record in to another table
1 AA
2 AB
3 B
4 C
...
Meaning if the record is duplicate, it will automatically add one more letter alphabetically.
Just a slightly different approach
Example
Declare #YourTable Table (ID int,[SomeCol] varchar(50))
Insert Into #YourTable Values
(1,'A')
,(2,'A')
,(3,'B')
,(4,'C')
Select *
,NewVal = concat(SomeCol,IIF(sum(1) over (partition by SomeCol)=1,'',char(64+row_number() over ( partition by SomeCol order by ID ))) )
From #YourTable
Returns
ID SomeCol NewVal
1 A AA
2 A AB
3 B B
4 C C
EDIT - Requested UPDATE
Declare #YourTable Table (ID int,[SomeCol] varchar(50))
Insert Into #YourTable Values
(1,'A')
,(2,'A')
,(3,'B')
,(4,'C')
Select *
,NewVal = concat(SomeCol,IIF(sum(1) over (partition by SomeCol)=1,'',replace(char(63+row_number() over ( partition by SomeCol order by ID )),'#','')) )
From #YourTable
Returns
ID SomeCol NewVal
1 A A
2 A AA
3 B B
4 C C
We might be able to handle this requirement with the help of a calendar table mapping secondary letters to duplicate sequence counts:
WITH letters AS (
SELECT 1 AS seq, 'A' AS let UNION ALL
SELECT 2, 'B' UNION ALL
SELECT 3, 'C' UNION ALL
...
SELECT 26, 'Z' UNION ALL
...
),
cte AS (
SELECT id, let, ROW_NUMBER() OVER (PARTITION BY let ORDER BY id) rn,
COUNT(*) OVER (PARTITION BY let) cnt
FROM yourTable
)
SELECT t1.id, t1.let + CASE WHEN t1.cnt > 1 THEN t2.let ELSE '' END AS let
FROM cte t1
LEFT JOIN letters t2
ON t1.id = t2.seq
ORDER BY t1.id;
Demo

Distribute a header table value to another table several rows with the same id

I need help with SQL Server on how to distribute a header table value to another table several rows with the same id : -
Table A : -
Id | Value
---+--------
1 | 40
2 | 21
Table B : -
Id | Qty
---+-------
1 | 20
1 | 13
2 | 1
Result should be : -
Id | Value | Qty
--------------------
1 | 20 | 20
1 | 20 | 13
2 | 21 | 1
WITH TblA
AS (SELECT 1 AS id,
40 AS num
UNION
SELECT 2,
21),
TblB
AS (SELECT 1 AS id,
20 AS num
UNION
SELECT 1,
13
UNION
SELECT 2,
1)
SELECT *
FROM TblA a
INNER JOIN TblB b ON a.id = b.id;
This below logic will distribute the value equally to all rows.
SELECT *,
(SELECT Value FROM A WHERE A.id = B.id)/COUNT(*) OVER(PARTITION BY id) Value
FROM B
Output is-
Id Qty Value
1 13 20
1 20 20
2 1 21
But if you wants to distribute the same value for all rows, do this below-
SELECT *,
(SELECT Value FROM A WHERE A.id = B.id) Value
FROM B
OR
SELECT B.*,A.Value
FROM B
INNER JOIN A ON B.id= A.id
Output will be-
Id Qty Value
1 20 40
1 13 40
2 1 21
Substitute table variables #Header & #Detail with your tables and remove the insert statements. You could also do this with a sub query to your main query as proposed in some of the other answers but for larger tables it may become slow hence the table variable called #Counter to first gather the aggregated count based on the #Detail table variable.
declare #Header table
(
Id int identity(1, 1),
[Value] int
)
declare #Detail table
(
Id int identity(1, 1),
HeaderId int,
Qty int
)
declare #Counter table
(
HeaderId int,
[Count] int
)
insert into #Header values (40)
insert into #Header values (21)
insert into #Detail values (1, 20)
insert into #Detail values (1, 13)
insert into #Detail values (2, 1)
insert into #Counter
select d.HeaderId, count(d.HeaderId)
from #Detail d
group by d.HeaderId
select
h.Id,
h.[Value] / c.[Count] [Value],
d.Qty
from #Header h
join #Detail d
on h.Id = d.HeaderId
join #Counter c
on h.Id = c.HeaderId
Please try with this simple Join
SELECT ta.id,
ta.value / (SELECT ( Count(tmp.id) )
FROM tb AS tmp
WHERE tmp.id = ta.id
GROUP BY tmp.id) AS 'value',
tb.qty
FROM ta
INNER JOIN tb
ON ta.id = tb.id
Full example (check Image):
CREATE TABLE ta
(
id INT,
value INT
)
CREATE TABLE tb
(
id INT,
qty INT
)
INSERT INTO ta
VALUES (1,40),
(2,21)
SELECT *
FROM ta
INSERT INTO tb
VALUES (1,20),
(1,13),
(2,1)
SELECT *
FROM tb
SELECT *
FROM ta
SELECT *
FROM tb
SELECT ta.id,
ta.value / (SELECT ( Count(tmp.id) )
FROM tb AS tmp
WHERE tmp.id = ta.id
GROUP BY tmp.id) AS 'value',
tb.qty
FROM ta
INNER JOIN tb
ON ta.id = tb.id
Just use a window function to divide the value:
select tb.id, ta.value / count(*) over (partition by ta.id) as value, tb.qty
from ta join
tb
on ta.id = tb.id;
Note that because these are integers, you might want to avoid integer division by using:
select tb.id, ta.value * 1.0 / count(*) over (partition by ta.id) as value, tb.qty
Here is a db<>fiddle.

Delete Rows based on two columns

How can I delete rows based on just two column conditions.
Example
Table 1
id name phone
1 aa 123
1 aa 345
1 bb 123
2 aa 456
1 NULL 123
1 123
My Expected output
id name phone
1 bb 123
2 aa 456
My condition to delete: if id and name is same, delete the rows
If one of the value in a condition is null or blank it should also delete the row as given in the input.
Delete from table1 t where exists (
Select * from
(Select id, name from table1 group by id, name having count(*) > 1) t2 where t.id = t2.id and t.name = t2.name)
This should do what you want. You can do the select first for testing purposes, then remove the Select and uncomment out the delete.
-- This joins on the table the set of data that has more then 1 row with duplicate IDs, and names. Then you can delete from here.
--DELETE t1
SELECT *
FROM Table1 T1
INNER JOIN (
-- this gets all the records that have more then 1 ID and Name that are the same.
SELECT ID, name
FROM Table1
GROUP BY ID, name
HAVING COUNT(*) > 1
) ToDelete ON T1.ID = ToDelete.ID
AND T1.name = ToDelete.name
create table #tablea (
id int,
name varchar(3),
phone int
)
insert into #tablea (id, name, phone)
values
(1,'aa','123'),
(1,'aa','345'),
(1,'bb','123'),
(2,'aa','456')
select * from #tablea
delete a
from #tablea a
inner join (
select id, name
from #tablea
group by id, name
having COUNT(*) > 1
) b on a.id = b.id and a.name = b.name
select * from #tablea
drop table #tablea

How to join two tables with unequal number of rows for same primary key value

I want to join two same tables with different data for the same primary key value.
I am performing a full join between the two tables, as I want to see what information has changed for the same ID between two months. I tried using a group by clause as well, but that didn't work.
Select
a.ID, a.Value1,
b.Value
from
TableA a
full join
TableB b on a.ID = b.ID
Input data:
Table A Table B
ID Value ID Value
--------- ----------
1 A 1 A
1 B 1 B
2 A 1 C
Desired output:
ID VALUE1 Value2
-----------------
1 A A
1 B B
1 Null C
Current (wrong) output:
ID VALUE1 Value2
-----------------
1 A A
1 A B
1 A C
1 B A
1 B B
1 B C
I suspect that ALL combinations are desired, thus FULL Join is better.
Select Case When a.ID IS Null Then b.ID Else a.ID End as ID,
a.Value,
b.Value
from UnequalRowsTableA a
full join UnequalRowsTableB b on a.ID=b.ID and a.Value = b.Value
Results
ID Value Value
1 A A
1 B B
2 A NULL
1 NULL C
You can use TableB in FROM then TableA in LEFT JOIN and in your ON clause add the Value matching too, to achieve your expected result:
SELECT B.ID,
A.Value AS Value1,
B.Value AS Value2
FROM TableB B
LEFT JOIN TableA A ON A.ID = B.ID AND A.Value = B.Value;
Demo with sample data:
DECLARE #TableA TABLE (ID INT, [Value] VARCHAR (1));
INSERT INTO #TableA (ID, [Value]) VALUES
(1, 'A'), (1, 'B'), (2, 'A');
DECLARE #TableB TABLE (ID INT, [Value] VARCHAR (1));
INSERT INTO #TableB (ID, [Value]) VALUES
(1, 'A'), (1, 'B'), (1, 'C');
SELECT B.ID,
A.[Value] AS Value1,
B.[Value] AS Value2
FROM #TableB B
LEFT JOIN #TableA A ON A.ID = B.ID AND A.[Value] = B.[Value];
Output:
ID Value1 Value2
----------------------
1 A A
1 B B
1 NULL C

Users with their child count

I have a table like this:
Userid Name ParentId
1 A Null
2 B 1
3 C 1
4 D 2
5 E 3
so output should be like this:
UserId Name Childs
1 A 2 // A has two children B and C
2 B 1 // B has one child D
3 C 1 // C has one child E
So please help and let me know if any confusion?
Try this
SELECT t1.UserId, t1.Name, count(t2.UserId)
FROM table t1
INNER JOIN table t2 ON t2.parentid = t1.UserId
GROUP BY t1.UserId, t1.Name
DECLARE #test Table
(ID INT, Name VARCHAR(20),CID INT)
INSERT INTO #test
VALUES
(1, 'A',NULL),
(2, 'B',1),
(3, 'C' ,1),
(1, 'D',2),
(2, 'E',3);
;WITH CTE AS
(
select ID,Name from #test
)
select c.ID,c.Name,COUNT(t.ID) from CTE C
INNER JOIN #test t
ON t.CID = c.ID
GROUP by c.ID,c.Name