Insert into Table B from Table A using Distinct - sql-server

I have 2 tables
Table A
NameID FirstName MiddleName LastName Addr1 Addr2 Phn1 Phn2 City State
NULL Micheal Calvin Dodson 12 23 1234 123 XYZ ABC
NULL John NULL Keith NULL NULL 2344 NULL SQE FDG
NULL John NULL Keith NULL NULL 2344 NULL SQE FDG
NULL William Stephen NULL 45 NULL NULL NULL HJD ABC
NULL Victor NULL Anthony NULL NULL NULL NULL NULL NULL
Table B
NameID FirstName MiddleName LastName Addr1 Addr2 Phn1 Phn2 City State Zip Email Gender...
I need to get the distinct records of (FirstName,MiddleName,LastName) of Table A and insert the same details along with the other fields matching with Table A into Table B.
My Table B has NameID as an identity coloum. So after inserting a unique record into Table B, I need to get that NameID and insert it back into Table A shown below :
TABLE A
Table A
NameID FirstName MiddleName LastName Addr1 Addr2 Phn1 Phn2 City State
1 Micheal Calvin Dodson 12 23 1234 123 XYZ ABC
2 John NULL Keith NULL NULL 2344 NULL SQE FDG
2 John NULL Keith NULL NULL 2344 NULL SQE FDG
3 William Stephen NULL 45 NULL NULL NULL HJD ABC
4 Victor NULL Anthony NULL NULL NULL NULL NULL NULL
TABLE B
NameID FirstName MiddleName LastName Addr1 Addr2 Phn1 Phn2 City State Zip Email Gender...
1 Micheal Calvin Dodson 12 23 1234 123 XYZ ABC NULL NULL NULL
2 John NULL Keith NULL NULL 2344 NULL SQE FDG NULL NULL NULL
3 William Stephen NULL 45 NULL NULL NULL HJD ABC NULL NULL NULL
4 Victor NULL Anthony NULL NULL NULL NULL NULL NULL NULL NULL NULL
Can you please help me with this. Im not able to get this query right. Code in SQL Server 2008
Thanks in advance,
Sunitha

I think the easiest way to do this is with two queries. The first problem is handling duplicates in TableA. The following query selects an arbitrary row for each name combination:
insert into TableB()
select ()
from (select a.*,
row_number() over (partition by FirstName, MiddleName, LastName order by FirstName) as seqnum
from TableA a
) a
where seqnum = 1
Then, update the original table:
update TableA
set NameId = (select max(NameId) from TableB
where TableB.FirstName = TableA.FirstName and
TableB.MiddleName = TableA.MiddleName and
TableB.LastName = TableA.LastName
)
where NameId is null
If your fields contain NULL values (rather than blanks), you can use coalesce() for the join conditions:
update TableA
set NameId = (select max(NameId) from TableB
where coalesce(TableB.FirstName, '<null>') = coalesce(TableA.FirstName, '<null>') and
coalesce(TableB.MiddleName, '<null>') = coalesce(TableA.MiddleName, '<null>') and
coalesce(TableB.LastName , '<null>')= coalesce(TableA.LastName, '<null>')
)
where NameId is null

DECLARE #results TABLE
(
NameID INT,
FirstName VARCHAR(32), -- guessing on data types for these columns
MiddleName VARCHAR(32),
LastName VARCHAR(32)
);
;WITH x AS
(
SELECT FirstName, MiddleName, LastName,
rn = ROW_NUMBER() OVER (PARTITION BY FirstName, MiddleName, LastName
ORDER BY (SELECT NULL)
) --, ... other columns ...
FROM dbo.TableA
)
INSERT dbo.TableB
(
FirstName, MiddleName, LastName --, ... other columns ...
)
OUTPUT
inserted.NameID, inserted.FirstName,
inserted.MiddleName, inserted.LastName
INTO #results
SELECT FirstName, MiddleName, LastName --, ... other columns ...
FROM x WHERE rn = 1;
UPDATE a SET NameID = r.NameID
FROM dbo.TableA AS a
INNER JOIN #results AS r
ON COALESCE(a.FirstName,'') = COALESCE(r.FirstName,'')
AND COALESCE(a.MiddleName,'') = COALESCE(r.MiddleName,'')
AND COALESCE(a.LastName,'') = COALESCE(r.LastName,'');

Related

How to use joins and make Normalised data to denormalised data

I have a database with the following tables :
UserTable :
-----UserID (int) PK,
-----UserName (Varchar) null
PhoneNumber :
------PhoID (int) PK,
------UserID(int) FK to UserTable
------PhoneNumber (Varchar) null
Address
------AddressID (int) PK,
------UserID(int) FK to UserTable
------AddressName (Varchar) null.
The users can have many phone numbers and many addresses. It is a one to many relationship.
I have the following data in these tables.
UserID UserName
1 Bridgerton
2 Merlin
3 Victoria
PhoID UserID PhoneNumber
1 1 phone1
2 1 phone2
3 1 phone3
4 2 Phone21
5 2 9909909900
AddressID UserID AddressName
1 1 Chennai
2 1 Gurgaon
3 2 Hyderabad
4 2 Mumbai
5 2 Gurgaon
Now I need the following result, want to see the userdetails, his phone number and the list of addressess in one go.
I wrote the following query,
select UserName, PhoneNumber, AddressName
from dbo.UserTable a
left outer join dbo.PhoneNumber b
on a.UserID=b.UserID
left outer join dbo.Address c
on a.UserID = c.UserID
Results
UserName PhoneNumber AddressName
Bridgerton phone1 Chennai
Bridgerton phone1 Gurgaon
Bridgerton phone2 Chennai
Bridgerton phone2 Gurgaon
Bridgerton phone3 Chennai
Bridgerton phone3 Gurgaon
Merlin phone21 Hyderabad
Merlin phone21 Mumbai
Merlin phone21 Gurgaon
Merlin 9909909900 Hyderabad
Merlin 9909909900 Mumbai
Merlin 9909909900 Gurgaon
Victoria NULL NULL
I know its messed up but need to see the username, userphone numbers and useraddress for a particular userid like this in just one record.
userid username Phonenumber Address
1 Bridgerton phone1, phone2, phone3 Gurgaon, Hyderabad
One option is to use UNION ALL to normalize your data then then perform a simple conditional aggregation/string_agg()
Example
;with cte as (
Select UserID
,Seq = UserID
,Col = 'UserName'
,Val = UserName
From UserTable
Union All
Select UserID
,Seq = PhoID
,Col = 'Phone'
,Val = PhoneNumber
From PhoneNumber
Union All
Select UserID
,Seq = AddressID
,Col = 'Address'
,Val = [AddressName]
From Address
)
Select UserID
,UserName = max(case when col='UserName' then Val end)
,PhoneNumber = string_agg( case when col='Phone' then Val end,',') within group ( order by seq)
,Address = string_agg( case when col='Address' then Val end,',') within group ( order by seq)
From cte
Group By UserID
Results
UserID UserName PhoneNumber Address
1 Bridgerton phone1,phone2,phone3 Chennai,Gurgaon
2 Merlin Phone21,9909909900 Hyderabad,Mumbai,Gurgaon
3 Victoria NULL NULL

How to Group Rows together in SQL Server?

I need to group the same ID into one row and keep the row containing the most data.
If an ID group have no data, I still want 1 row returned. I have roughly 30 data columns.
Example:
ID
City
Country
Data A
Data B
Data30
1
City1
Country1
DataA1
DataB1
DataN1
1
City2
Country2
Null
Null
Null
1
City3
Country3
Null
Null
Null
2
City1
Country1
DataA1
DataB1
DataN1
2
City2
Country2
Null
Null
Null
2
City3
Country3
Null
Null
Null
3
City1
Country1
Null
Null
Null
3
City2
Country2
Null
Null
Null
3
City3
Country3
Null
Null
Null
Result:
ID
City
Country
Data A
Data B
Data30
1
City1
Country1
DataA1
DataB1
DataN1
2
City1
Country1
DataA1
DataB1
DataN1
3
City1
Country1
Null
Null
Null
Any suggestion would greatly be appreciated!
This is easy to do if you sum the number of non-null columns and then apply a row_number
with cte as (
select *, Row_Number() over(partition by id order by tot desc) rn
from (
select * , Iif(data1 is null,0,1) + Iif(data2 is null,0,1) + Iif(data30 is null,0,1) tot
from t
)x
)
select id, city, country, data1, data2, data30
from cte
where rn=1
See Working demo

SSRS - Struggling with Grouping

I've got a table that looks like this...
I need to create a report like this
but cant figure out how to create the columns in SSRS... any help would be greatly appreciated.
With a slight change to your data you can easily do this in SSRS.
Assuming your data is currently in a table that looks like example then you can use something like this in your dataset query..
SELECT Account, SalesPerson, Cost, Product, 'Expected' as Measure, ExpectedCloseDate as ActivityDate
FROM myTable WHERE ExpectedCloseDate IS NOT NULL
UNION ALL
SELECT Account, SalesPerson, Cost, Product, 'Actual' as Measure, ActualCloseDate as ActivityDate
FROM myTable WHERE ActualCloseDate IS NOT NULL
Then in your report, add a Matrix
Add a RowGroup that Groups by Account, SalesPerson and Product
Add a Column Group that groups by Measure
Add a Parent Column Group to the Measure column group that groups by
Year and Month
Once this is done, drop the Cost field onto the matrix as the data field and that should be that other than a bit of formatting on the date.
If you are still struggling, let me know and I will post a full answer, I'm not at my PC at the moment (and it's midnight!)
Not an expert on SSRS reports, but the actual data pivoting can be done in SQL. The solution below uses Common Table Expression's (CTE's) and the pivot keyword. The solution is not the most dynamic one, but it covers your example data.
-- create sample data
declare #data table
(
Account nvarchar(15),
SalesPerson nvarchar(15),
Cost int,
Product nvarchar(15),
ExpectedCloseDate date,
ActualCloseDate date
);
insert into #data (Account, SalesPerson, Cost, Product, ExpectedCloseDate, ActualCloseDate) values
('ABC House', 'John Doe', 120, 'Blocks', '2020-02-03', '2020-02-03'),
('Pizza House', 'Jose Guava', 10, 'Boxes', '2020-04-04', '2020-04-24'),
('The Hanger', 'John Doe', 10, 'Wings', '2020-02-03', '2020-06-24'),
('The Store', 'John Doe', 10, 'Catnip', '2020-02-03', null ),
('The Store', 'Jose Guava', 15, 'Boxes', '2020-03-04', '2020-05-24'),
('WangTai', 'Pete Ringer', 20, 'Lettuce', '2020-04-01', '2020-04-04'),
('WangTai', 'Pete Ringer', 22, 'Paper Plates', '2020-05-01', null ),
('Woolies', 'Pete Ringer', 20, 'Catnip', '2020-03-04', '2020-03-04');
Solution overview:
Convert the dates to months.
Pivot the expected costs.
Pivot the actual costs.
Join everything together.
Solution:
-- solution
with dataMonths as
(
select d.Account,
d.SalesPerson,
d.Product,
d.Cost,
month(d.ExpectedCloseDate) as 'ExpectedMonth',
month(d.ActualCloseDate) as 'ActualMonth'
from #data d
),
dataExpected as
(
select p.Account,
p.SalesPerson,
p.Product,
[2] as 'ExpFeb20',
[3] as 'ExpMar20',
[4] as 'ExpApr20',
[5] as 'ExpMay20',
[6] as 'ExpJun20',
[7] as 'ExpJul20'
from dataMonths dm
pivot ( sum(dm.Cost) for ExpectedMonth in ([2], [3], [4], [5], [6], [7]) ) p
),
dataActual as
(
select p.Account,
p.SalesPerson,
p.Product,
[2] as 'ActFeb20',
[3] as 'ActMar20',
[4] as 'ActApr20',
[5] as 'ActMay20',
[6] as 'ActJun20',
[7] as 'ActJul20'
from dataMonths dm
pivot ( sum(dm.Cost) for ActualMonth in ([2], [3], [4], [5], [6], [7]) ) p
)
select dm.Account,
dm.SalesPerson,
dm.Product,
de.ExpFeb20,
da.ActFeb20,
de.ExpMar20,
da.ActMar20,
de.ExpApr20,
da.ActApr20,
de.ExpMay20,
da.ActMay20,
de.ExpJun20,
da.ActJun20,
de.ExpJul20,
da.ActJul20
from dataMonths dm
join dataExpected de
on de.Account = dm.Account
and de.SalesPerson = dm.SalesPerson
and de.Product = dm.Product
join dataActual da
on da.Account = dm.Account
and da.SalesPerson = dm.SalesPerson
and da.Product = dm.Product;
This gives me something quite close to the final report:
Account SalesPerson Product ExpFeb20 ActFeb20 ExpMar20 ActMar20 ExpApr20 ActApr20 ExpMay20 ActMay20 ExpJun20 ActJun20 ExpJul20 ActJul20
--------------- --------------- --------------- ----------- ----------- ----------- ----------- ----------- ----------- ----------- ----------- ----------- ----------- ----------- -----------
ABC House John Doe Blocks 120 120 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL
Pizza House Jose Guava Boxes NULL NULL NULL NULL 10 10 NULL NULL NULL NULL NULL NULL
The Hanger John Doe Wings 10 NULL NULL NULL NULL NULL NULL NULL NULL 10 NULL NULL
The Store John Doe Catnip 10 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL
The Store Jose Guava Boxes NULL NULL 15 NULL NULL NULL NULL 15 NULL NULL NULL NULL
WangTai Pete Ringer Lettuce NULL NULL NULL NULL 20 20 NULL NULL NULL NULL NULL NULL
WangTai Pete Ringer Paper Plates NULL NULL NULL NULL NULL NULL 22 NULL NULL NULL NULL NULL
Woolies Pete Ringer Catnip NULL NULL 20 20 NULL NULL NULL NULL NULL NULL NULL NULL

T-SQL Get Values from Lookup table and use in view / stored procedure

I couldn't find that via search, so I guess I am not asking it the right way, so help is welcome.
We have a lookup table:
Id Name
------------------
1 "Test"
2 "New"
3 "InProgress"
Table2:
StatusId SomethingElse
1
2
Table 1
ID Other Other StatusId (Fkey to Table2) ...
Then we have a view that selects from several tables and one of the columns is a CASE Statement:
SELECT * FROM Table1 t1 -- has million records
CASE When t1.StatusId = 1 THEN (SELECT Name from LOOKUP table where ID = 1) END --'Test'
CASE When t1.StatusId = 2 THEN (SELECT Name from LOOKUP table where ID = 2) END --'New'
CASE When t3.Date is not null THEN (SELECT Name from LOOKUP table where ID = 3) END --'In Progress'
-- AND ALSO the case look at other tables another 5-6 tables and there are conditions from there
INNER JOIN Table2 t2 on ...
INNER JOIN Table3 t3 on ...
As you see these are really static values.
I want to load them once into variables, e.g.
#LookUp1 = SELECT [NAME] FROM LookUP WHERE Id = 1,
#LookUp2 = SELECT [NAME] FROM LookUP WHERE Id = 2
and replace the select in the CASE statement to this:
When StatusId = 1 THEN #LookUp
When StatusId = 2 THEN #LookUp2
The view loops through millions of records and it gets really slow to do the select from Lookup table for every row.
Why not simply use a join?
SELECT <columns list from main table>, Lt.Name
FROM <main table> As Mt -- Do not use such aliases in real code!
JOIN <SecondaryTable> As St -- this represents your Table3
ON <condition>
[LEFT] JOIN <Lookup table> As Lt
ON Mt.StatusId = Lt.Id
OR (Lt.Id = 3 AND St.Date is not null)
Of course, replace <columns list from main table> with the actual columns list, <main table> with the name of the main table and so on.
The join might be an inner or left join, depending on the nullability of the StatusId column in the main table and if it's nullable, on what you want to get in such cases (either a row with null name or no row at all).
I've put together a little demonstration to show you exactly what I mean.
Create and populate sample tables (Please save us this step in your future questions):
CREATE TABLE LookUp (Id int, Name varchar(10));
INSERT INTO LookUp (Id, Name) VALUES
(1, 'Test'), (2, 'New'), (3, 'InProgress');
CREATE TABLE Table1 (Id int not null, StatusId int null);
INSERT INTO Table1(Id, StatusId)
SELECT n, CASE WHEN n % 3 = 0 THEN NULL ELSE (n % 3) END
FROM
(
SELECT TOP 30 ROW_NUMBER() OVER(ORDER BY ##SPID) As n
FROM sys.objects
) tally
CREATE TABLE Table3
(
Id int not null,
Date date null
)
INSERT INTO Table3 (Id, Date)
SELECT Id, CASE WHEN StatusId IS NULL AND Id % 4 = 0 THEN GetDate() END
FROM Table1
The query:
SELECT Table1.Id,
Table1.StatusId,
Table3.Date,
LookUp.Name
FROM Table1
JOIN Table3
ON Table1.Id = Table3.Id
LEFT JOIN LookUp
ON Table1.StatusId = LookUp.Id
OR (LookUp.Id = 3 AND Table3.Date IS NOT NULL)
Results:
Id StatusId Date Name
1 1 NULL Test
2 2 NULL New
3 NULL NULL NULL
4 1 NULL Test
5 2 NULL New
6 NULL NULL NULL
7 1 NULL Test
8 2 NULL New
9 NULL NULL NULL
10 1 NULL Test
11 2 NULL New
12 NULL 27.06.2019 InProgress
13 1 NULL Test
14 2 NULL New
15 NULL NULL NULL
16 1 NULL Test
17 2 NULL New
18 NULL NULL NULL
19 1 NULL Test
20 2 NULL New
21 NULL NULL NULL
22 1 NULL Test
23 2 NULL New
24 NULL 27.06.2019 InProgress
25 1 NULL Test
26 2 NULL New
27 NULL NULL NULL
28 1 NULL Test
29 2 NULL New
30 NULL NULL NULL
You can also see a live demo on rextester.
Create a SQL function which return Name according to Id.
Create FUNCTION [dbo].[GetLookUpValue]
(
#Id int
)
RETURNS varchar(500)
AS BEGIN
return(Select Name from LOOKUP_table with(nolock) where Id=#Id)
END

T-SQL return a row if another row doesn't exist

Let's say I have this table (this is simplified, of course there are other columns):
CompanyID (int)
ContactName (varchar(50))
ContactType (char(1))
with the values:
CompanyID | ContactName | ContactType
-----------------------------------------------
1 | John Doe | A
1 | Jane Smith | B
2 | Ralph Jones | B
3 | Dick Grayson | A
What I want are all the companies where there's a ContactType='A', unless there is no ContactType='A' return the ContactType='B'. So in this example, I want:
1, John Doe (because he's a ContactType A)
2, Ralph Jones (because Company #2 doesn't have a ContactType A)
3, Dick Grayson (because he's a ContactType A)
I can't just say "A or B" because a company may have both.
Here's what I tried (and failed)
use MyFancyDatabase
drop table #TypeA
drop table #TypeB
drop table #TypeAB
create table #TypeA(ownerkey int, ContactName varchar(200), ContactType char(1))
insert #TypeA
Select ownerkey, ContactName, ContactType from address
where ContactType = 'A' and CancelDate is null
create table #TypeB(ownerkey int, ContactName varchar(200), ContactType char(1))
insert #TypeB
Select ownerkey, ContactName, ContactType from address
where ContactType = 'B' and CancelDate is null
create table #TypeAB(ownerkey int, ContactName varchar(200), ContactType char(1))
insert #TypeAB
select * from #TypeA
except
select * from #TypeB
I guess in English it's "A, but if there is no A, then take B."
Any suggestions?
SELECT a.OwnerKey, a.CompanyName, Case WHEN a.ContactType IS NULL THEN b.ContactType ELSE a.ContactType END AS ContactType
FROM #TypeA a
LEFT JOIN #TypeB b on a.OwnerKey = b.OwnerKey
I think this should work for you.
with SortedResults as
(
select CompanyID
, ContactName
, ContactType
, ROW_NUMBER() over (partition by CompanyID order by ContactType) as RowNum
from ThisTable
)
select *
from SortedResults
where RowNum = 1
Try this SQL
Select t1.CompanyID ,
ContactName = IIF(t1.ContactType='A',t1.ContactName,t2.ContactName)
ContactType = IIF(t1.ContactType='A','A',t2.ContactType)
FROM address as t1 left join address as t2
on t1.CompanyID = t2.CompanyID
AND t1.ContactName = t2.ContactName
AND (t1.ContactType <> t2.ContactType)
IF you have more type than A OR B and you want just A and B add this on the where statment
WHERE (t1.ContactType = 'A' OR t1.ContactType = 'B')
AND (t2.ContactType = 'A' OR t2.ContactType = 'B')
The columns in your defined table don't appear to match with the ones in your example query. I'm guessing OwnerKey is the same as CompanyID?
If so, and you keep most of the rest of your code, the last select would need to be:
select * from #TypeA
union all
select * from #TypeB
where not exists (
select *
from #TypeA
where #TypeA.ownerkey = #TypeB.ownerkey)

Resources