How to get unique records based on 2 tables? - sql-server

Hi I have doubt in sql server
Table : emp
Id | Desc
1 | abc
2 | def
3 | har
table2 : emp1
Id | Desc
3 | Har
4 | jai
4 | jai
5 | uou
6 | uni
6 | udkey
2 | Jainiu
based on above table I want output like below
ID | Desc
1 | abc
2 | def
3 | har
4 | jai
5 | uou
6 | uni
I tried like below
select id, desc from emp
union
select * from (select *,row_number()over(partition by id)as rn from emp1)
where rn=1
after executing this query I got an error like below
Msg 205, Level 16, State 1, Line 2
All queries combined using a UNION, INTERSECT or EXCEPT operator must have an equal number of expressions in their target lists.
It's saying above 1st query are 2 column and 2 query are 3 column, this process
how we avoid this rn column.
please tell me how to write query to achive this task in sql server

Use:
SELECT id,
desc
FROM emp
UNION
SELECT id,
desc
FROM emp1
Union will automatically do a distinct and sort it in the proper order, you don't need to do anything, no windwos functions required
Result:
ID | Desc
1 | abc
2 | def
2 | Jainiu
3 | har
4 | jai
5 | uou
6 | uni
6 | udkey
If you want to remove udkey and Jainiu please mention a logi to choose between def and Jainiu and so on...

using the Union or Distinct we can reomve duplicates and row_number we can get output and In union should have equal number of columns
DECLARE #emp TABLE
([Id] int, [Desc] varchar(3))
;
INSERT INTO #emp
([Id], [Desc])
VALUES
(1, 'abc'),
(2, 'def'),
(3, 'har')
DECLARE #emp1 TABLE
([Id] int, [Desc] varchar(6))
INSERT INTO #emp1
([Id], [Desc])
VALUES
(3, 'Har'),
(4, 'jai'),
(4, 'jai'),
(5, 'uou'),
(6, 'uni'),
(6, 'udkey'),
(2, 'Jainiu')
;with CTE AS (
select
id,
[Desc],
Row_NUMBER()OVER(PARTITION BY ID ORDER BY ID,[Desc]DESC)RN
from
(select distinct id,[Desc] from #EMP
UNION ALL
select distinct id,[Desc] from #EMP1)T)
select id,[Desc] from CTE
WHERE RN = 1

Related

SQL Server split field and create addition rows with values from main row

I have a table with rows and in one field there are values like this A,B,C
Table 'Mytable':
|ID | Date | MyValue | SplitID |
|1 | 2019-12-17 | A | |
|2 | 2019-12-15 | A,B | |
|3 | 2019-12-16 | B,C | |
Result should be:
|1 | 2019-12-17 | A | 1 |
|2 | 2019-12-15 | A | 2 |
|4 | 2019-12-15 | B | 2 |
|3 | 2019-12-16 | B | 3 |
|5 | 2019-12-16 | C | 3 |
(Sorry, I could not find HOW to format a table in the Stackoverflow help)
I tried a inline table function which splits the Field Myvalue into more lines but could not pass my rows with
charindex(',',[MyValue])>0
from MyTable as input lines.
The code is this:
ALTER function [dbo].[fncSplitString](#input Varchar(max), #Splitter Varchar(99), #ID int)
returns table as
Return
with tmp (DataItem, ix, ID) as
( select LTRIM(#input) , CHARINDEX('',#Input), #ID --Recu. start, ignored val to get the types right
union all
select LTRIM(Substring(#input, ix+1,ix2-ix-1)), ix2, #ID
from (Select *, CHARINDEX(#Splitter,#Input+#Splitter,ix+1) ix2 from tmp) x where ix2<>0
) select DataItem,ID from tmp where ix<>0
Thanks for help
Michael
You can try the following query.
Create table #Temp(
Id int,
DateField Date,
MyValue Varchar(10),
SplitID int
)
CREATE FUNCTION [dbo].[SplitPra] (#Value VARCHAR(MAX), #delimiter CHAR)
RETURNS #DataResult TABLE([Position] TINYINT IDENTITY(1,1),[Value] NVARCHAR(128))
AS
BEGIN
DECLARE #XML xml = N'<r><![CDATA[' + REPLACE(#Value, #delimiter, ']]></r><r><![CDATA[') + ']]></r>'
INSERT INTO #DataResult ([Value])
SELECT RTRIM(LTRIM(T.c.value('.', 'NVARCHAR(128)')))
FROM #xml.nodes('//r') T(c)
RETURN
END
insert into #Temp Values(1, '2019-12-17', 'A', NULL),(2, '2019-12-15', 'A,B', NULL), (3, '2019-12-16', 'B,C', NULL)
Select
#Temp.Id, DateField, b.Value as MyValue, b.Id as SplitValue
from #Temp inner join (
select
Id, f.*
from
#Temp u
cross apply [dbo].[SplitPra](u.MyValue, ',') f
)b on #Temp.Id = b.Id
Drop table #Temp
This will give an output as shown below.
Id DateField MyValue SplitValue
---------------------------------
1 2019-12-17 A 1
2 2019-12-15 A 2
2 2019-12-15 B 2
3 2019-12-16 B 3
3 2019-12-16 C 3
You can find the live demo here.
I found this solution, i hope it will work for you. But i didn't use your function to solve this problem. Instead of that, i used cross apply function.You can find the query below:
-- Creating Test Table
CREATE TABLE #Test
(
ID int,
Date date,
MyValue nvarchar(max),
SplitID int
);
GO
-- Inserting data into test table
INSERT INTO #Test VALUES (1, '2019-12-17', 'A', NULL);
INSERT INTO #Test VALUES (2, '2019-12-15', 'A,B', NULL);
INSERT INTO #Test VALUES (3, '2019-12-16', 'B,C', NULL);
GO
-- Select query
SELECT
*,
(SELECT ID FROM test t1 WHERE t.Date = t1.date) AS SplitID
FROM
(
SELECT
ROW_NUMBER() OVER (ORDER BY ID) AS ID,
Date,
substring(A.value,1,
CASE WHEN charindex(',',rtrim(ltrim(A.value))) = 0 then LEN(A.value)
ELSE charindex(',',rtrim(ltrim(A.value))) -1 end) as MyValue
FROM Test
CROSS APPLY string_split (MyValue, ',') A) AS T
ORDER BY MyValue ASC;
And the result must be like that:
ID Date MyValue SplitID
1 2019-12-17 A 1
2 2019-12-15 A 2
3 2019-12-15 B 2
4 2019-12-16 B 3
5 2019-12-16 C 3

How can I take the sum of only the max values?

I need to take the max cost of each tracking number (TN) and then sum those values grouped by the OrderNo.
Here's a table:
+----+-----+-------+
|TNo |cost| OrderNo|
+----+-----+-------+
| 1 | 5 | 12 |
| 1 | 4 | 12 |
| 2 | 6 | 12 |
| 2 | 3 | 12 |
| 3 | 3 | 15 |
| 4 | 2 | 15 |
| 4 | 3 | 15 |
+----+-----+-------+
Here's what I want my results to be:
+--------+-----+
| OrderNo| Sum |
+--------+-----+
| 12 | 11 | (6+5)
| 15 | 6 | (3+3)
+--------+-----+
This is what I have so far, but this sums the max but for all instances of the Tracking No. For example, in the above table, for Order# 12, it would sum 5+5+6+6. I only want to sum the max values (5+6).
SELECT ol.OrderNo, SUM(t.maxCost)
FROM (
SELECT
ol.TrackingNumber, MAX(ol.Cost) maxCost
FROM OzLink ol GROUP BY ol.TrackingNumber) t
JOIN OzLink ol ON ol.TrackingNumber=t.TrackingNumber
GROUP BY ol.OrderNo
**Also, I'm new to this work and asking questions on stackoverflow so feedback on how I asked this question would be appreciated!
you could do it like this:
SELECT ol.OrderNo, SUM(ol.maxCost)
FROM (
SELECT
ol.TrackingNumber, MAX(ol.Cost) maxCost, ol.OrderNo
FROM OzLink ol GROUP BY ol.TrackingNumber,ol.OrderNo) ol
GROUP BY ol.OrderNo
You can benefit from cte like below:
CREATE TABLE mytab
(
TNo INT,
Cost INT,
OrderNo INT
)
insert into mytab values (1,5,12)
insert into mytab values (1,4,12)
insert into mytab values (2,6,12)
insert into mytab values (2,3,12)
insert into mytab values (3,3,13)
insert into mytab values (4,2,13)
insert into mytab values (4,3,13)
;with cte (TNo,OrderNo,maxcost) as (
select TNo,OrderNo,Max(Cost) as maxcost
from mytab
group by TNo, OrderNo
)
select OrderNo,SUM(maxcost)
from cte
group by OrderNo
There is a few ways, like the answers below. But you can also use the below query, and create a Row number based on OrderNo and TN and Order by the Cost DESC in the Subquery and then only return the highest cost.
SELECT OrderNo,
SUM(Cost) As Cost
FROM
(
SELECT ROW_NUMBER() OVER(PARTITION BY OrderNo, TN ORDER BY Cost DESC) AS HighestCost,
Cost,
OrderNo,
TN
FROM TableName
) AS Data
WHERE HighestCost = 1
GROUP BY OrderNo
Same as another answer
declare #T TABLE (TNo INT, Cost INT, OrderNo INT);
insert into #T values (1,5,12), (1,4,12), (2,6,12), (2,3,12), (3,3,15), (4,2,15), (4,3,15);
select t.OrderNo, sum(t.cost)
from ( select OrderNo, cost
, ROW_NUMBER() over (partition by TNo, OrderNo order by cost desc) as rn
from #T
) t
where t.rn = 1
group by t.OrderNo;
OrderNo
----------- -----------
12 11
15 6

Pivot Function on Multiple Tables and Dynamic Columns in SQL

I have 3 tables in Table 2 we have columns with columnName Field they can grow Dynamically at that time we have just 5 columns for Each CTypeId they can be 6 or 10 etc. In Table3 we have the column values.
For example AccountManager From Table 2 have value in Table 3 Jack / Kate
similarly other columns and their values are
ColumnName | Values
Channel | PS
StartDate | 06/03/2017
I want Result Like this
I have tried using Pivot Function with the following query:
Declare #Columns nvarchar(max)
Declare #a nvarchar(max)
Set #Columns = (select STUFF((select ',' + '[' + Convert(varchar(200), ColumnName) + ']' from CharityTypeInformationDynamicFields FOR XML PATH('')), 1,1, ''))
Declare #sql nvarchar(max)
= 'Select *
from
(select cd.Id, cd.Value, ci.ColumnName
from Table3 cd
Inner Join Table2 ci
on ci.Id = cd.DynamicFieldID
) as s
Pivot(MAX(Value) ForColumnName IN ('+#columns+')) as pvt'
Select #sql
But the query gives the result:
What do I need to change to achieve my desired output?
There are a few issues that you need to solve in order to get the result you desire. But before trying a dynamic sql version of a query I'd always recommend that you try get your final result by writing a hard-coded or static version first. This allows you to get the desired result without bugs and then convert it to dynamic sql as your final query.
First, let's get your table structures and sample data into a reusable script. It appears that you only need table2 and table3 to get your end result:
create table #table2
(
id int,
ctypeid int,
columnname varchar(50)
)
insert into #table2
values
(1, 20, 'Account Manager'), (2, 20, 'Channel'),
(3, 20, 'Start Date'), (4, 20, 'End Date'),
(5, 20, 'Gross Annual'), (6, 6, 'Account Manager'),
(7, 6, 'Channel'), (8, 6, 'Start Date'),
(9, 6, 'End Date'), (10, 6, 'Gross Annual');
create table #table3
(
id int,
table2id int,
value varchar(50)
)
insert into #table3
values
(1, 1, 'Jack / Kate'), (2, 2, 'PS'), (3, 3, '06/03/2017'),
(4, 4, '07/03/2017'), (5, 5, '2500'), (6, 6, 'Ollie'),
(7, 7, 'D2D'), (8, 8, '06/03/2017'), (9, 9, '06/03/2017'),
(10, 10, '5232'), (11, 1, 'Jack'), (12, 2, 'PSP'),
(13, 3, '06/03/2017'), (14, 4, '07/03/2017'), (15, 5, '7000'),
(16, 1, 'Jack Sparrow'), (17, 2, 'PS Sparrow'), (1, 3, '06/03/2017'),
(19, 4, '07/03/2017'), (20, 5, '3000'), (21, 6, 'John'),
(22, 7, 'JEDF'), (23, 8, '06/03/2017'), (24, 9, '06/03/2017'),
(25, 10, '5232');
Next, you need to write your PIVOT query. Your final result only includes the values from 3 columns CTypeId, Value, and ColumnName, so the start of your query PIVOT would be:
select
CTypeId,
[Account Manager], [Channel], [Start Date],
[End Date], [Gross Annual]
from
(
select ci.CTypeId, cd.Value, ci.ColumnName
from #Table3 cd
Inner Join #Table2 ci
on ci.Id = cd.Table2Id
) d
pivot
(
max(Value)
for ColumnName in ([Account Manager], [Channel], [Start Date],
[End Date], [Gross Annual])
) piv
Demo. But since you're aggregating string values in the Value column, you will only return one row for each CTypeId:
+---------+-----------------+---------+------------+------------+---------------+
| CTypeId | Account Manager | Channel | Start Date | End Date | Gross Annual |
+---------+-----------------+---------+------------+------------+---------------+
| 6 | Ollie | JEDF | 06/03/2017 | 06/03/2017 | 5232 |
| 20 | Jack Sparrow | PSP | 06/03/2017 | 07/03/2017 | 7000 |
+---------+-----------------+---------+------------+------------+---------------+
which is not what you want, so you need to do something to allow for multiple rows. If you look at a sample of the data that is returned by the subquery:
+---------+-------------+------------------+
| CTypeId | Value | ColumnName |
+---------+-------------+------------------+
| 20 | Jack / Kate | Account Manager |
| 20 | PS | Channel |
| 20 | 06/03/2017 | Start Date |
| 20 | 07/03/2017 | End Date |
| 20 | 2500 | Gross Annual |
| 6 | Ollie | Account Manager |
| 6 | D2D | Channel |
| 6 | 06/03/2017 | Start Date |
| 6 | 06/03/2017 | End Date |
| 6 | 5232 | Gross Annual |
+---------+-------------+------------------+
You'll see that you have unique data over a combination of CTypeId and ColumnName values, so you can create a unique row number using the windowing function row_number in your subquery which can be used to uniquely group the data for a pivot. By changing the above PIVOT code to:
select
CTypeId,
[Account Manager], [Channel], [Start Date],
[End Date], [Gross Annual]
from
(
select ci.CTypeId, cd.Value, ci.ColumnName,
rn = row_number() over(partition by ci.CTypeId, ci.ColumnName order by cd.Value)
from #Table3 cd
Inner Join #Table2 ci
on ci.Id = cd.Table2Id
) d
pivot
(
max(Value)
for ColumnName in ([Account Manager], [Channel], [Start Date],
[End Date], [Gross Annual])
) piv
order by CTypeId
See demo, you get the desired result:
+---------+-----------------+------------+------------+------------+---------------+
| CTypeId | Account Manager | Channel | Start Date | End Date | Gross Annual |
+---------+-----------------+------------+------------+------------+---------------+
| 6 | John | D2D | 06/03/2017 | 06/03/2017 | 5232 |
| 6 | Ollie | JEDF | 06/03/2017 | 06/03/2017 | 5232 |
| 20 | Jack | PS | 06/03/2017 | 07/03/2017 | 2500 |
| 20 | Jack / Kate | PS Sparrow | 06/03/2017 | 07/03/2017 | 3000 |
| 20 | Jack Sparrow | PSP | 06/03/2017 | 07/03/2017 | 7000 |
+---------+-----------------+------------+------------+------------+---------------+
Once you've got your final result you want, it's easy to convert the query to dynamic SQL:
Declare #Columns nvarchar(max)
Declare #a nvarchar(max)
Set #Columns = stuff((select distinct ',' + quotename(ColumnName)
from #table2
for xml path(''), type).value('.', 'nvarchar(max)'), 1, 1, '');
Declare #sql nvarchar(max)
= 'Select CTypeId, '+#Columns+'
from
(
select ci.CTypeId, cd.Value, ci.ColumnName,
rn = row_number() over(partition by ci.CTypeId, ci.ColumnName order by cd.Value)
from #Table3 cd
Inner Join #Table2 ci
on ci.Id = cd.Table2Id
) as s
Pivot(MAX(Value) For ColumnName IN ('+#columns+')) as pvt
order by CTypeId'
execute(#sql);
See Demo. This gives the same result as the hard-coded version with the flexibility of dynamic sql.

SQL Server 2012 - Looking for duplicates with differences

In SQL Server 2012, I have a table like this:
Id | AccountID | Accession | Status
----------------------------------------
1 | 1234567 | ABCD | F
2 | 1234567 | ABCD | F
3 | 2345678 | BCDE | F
4 | 8765432 | BCDE | F
5 | 3456789 | CDEF | F
6 | 9876543 | CDEF | A
I need to find rows that have the same Accession and a Status of "F", but a different AccountID.
I need a query that would return:
Id | AccountID | Accession | Status
----------------------------------------
3 | 2345678 | BCDE | F
4 | 8765432 | BCDE | F
1 and 2 wouldn't be returned because they have the same AccountID. 5 and 6 wouldn't be returned because the status on 6 is "A" and not "F".
You could do something like this.
;WITH NonDupAccountIDs AS
(
SELECT AccountID,Accession, Status
FROM MyTable
WHERE Status = 'F'
GROUP BY AccountID,Accession, Status
HAVING COUNT(Id) = 1
)
,DupAccessions AS
(
SELECT Accession
FROM MyTable
WHERE Status = 'F'
GROUP BY Accession
HAVING COUNT(AccountID) > 1
)
select a.AccountID, a.Accession, a.Status
FROM NonDupAccountIDs a
INNER JOIN DupAccessions b
ON a.Accession = b.Accession
Another alternative
Declare #Table table (id int,AccountID varchar(25),Accession varchar(25),Status varchar(25))
Insert into #Table (id , AccountID , Accession , Status) values
(1, 1234567,'ABCD','F'),
(2, 1234567,'ABCD','F'),
(3, 2345678,'BCDE','F'),
(4, 8765432,'BCDE','F'),
(5, 3456789,'CDEF','F'),
(6, 9876543,'CDEF','A')
Select A.*
from #Table A
Join (
Select Accession
From #Table
Where Status='F'
Group By Accession
Having Min(Accession)=Max(Accession)
and count(Distinct AccountID)>1
) B on a.Accession=B.Accession
Returns
id AccountID Accession Status
3 2345678 BCDE F
4 8765432 BCDE F
This works as well. If there are multiple sets of duplicates, this only returns one with the highest ID. Example
John Cappelletti had a great solution as well, his returns all duplicated values if there exists any incongruity. Example
I had to add some more data to see what would happen. You should decide how you will treat these occurrences.
select
max(ID) ID,AccountID, Accession
from p where Status = 'F'
group by AccountID, Accession
having
(select count(Accession) from (select max(ID) ID,AccountID, Accession from p where Status = 'F' group by AccountID, Accession) f where f.accession = p.accession)>1
;
SELECT t2.Id, t1.AccountID, t1.Accession, t1.Status
FROM TABLE_NAME t2
INNER JOIN (
SELECT AccountID, Accession, Status
FROM TABLE_NAME
GROUP BY Status, Accession, AccountID
) t1
ON t1.AccountID = t2.AccountID
Might need to play with this but should get you close. Remember to replace TABLE_NAME with your table.

Split column with delimited values for an inner join

I need to perform an inner join to a column containing delimited values like:
123;124;125;12;3433;35343;
Now what I am currently doing is this:
ALTER procedure [dbo].[GetFruitDetails]
(
#CrateID int
)
AS
SELECT Fruits.*, Fruits_Crates.CrateID
FROM Fruits_Crates INNER JOIN Fruits
ON Fruits_Crates.FruitID = Fruits.ID
WHERE Fruits_Crates.CrateID = #CrateID
Now the issue is I am saving the data this way:
FruitCrateID FruitID
1 1;
2 1;2;3;4
3 3;
How can I inner join FruitsIDs to the fruit table to get fruit details as well?
Using the method posted in this answer, you can convert the delimited string into rows of a temp table and then join to that:
SQL Fiddle
Schema Setup:
CREATE TABLE Fruits_Crates
([FruitCrateID] int, [FruitID] varchar(10))
;
INSERT INTO Fruits_Crates
([FruitCrateID], [FruitID])
VALUES
(1, '1;'),
(2, '1;2;3;4;'),
(3, '3;')
;
CREATE TABLE Fruits
([FruitID] int, [FruitName] varchar(10))
;
INSERT INTO Fruits
([FruitID], [FruitName])
VALUES
(1, 'Apple'),
(2, 'Banana'),
(3, 'Orange'),
(4, 'Pear')
;
Insert to temp table:
SELECT A.[FruitCrateID],
Split.a.value('.', 'VARCHAR(100)') AS FruitId
INTO #fruits
FROM (SELECT [FruitCrateID],
CAST ('<M>' + REPLACE([FruitID], ';', '</M><M>') + '</M>' AS XML) AS String
FROM Fruits_Crates) AS A CROSS APPLY String.nodes ('/M') AS Split(a)
Join temp table to lookup:
SELECT t1.*, t2.FruitName
FROM #Fruits t1
INNER JOIN Fruits t2 on t1.FruitId = t2.FruitId
Results:
| FRUITCRATEID | FRUITID | FRUITNAME |
|--------------|---------|-----------|
| 1 | 1 | Apple |
| 2 | 1 | Apple |
| 2 | 2 | Banana |
| 2 | 3 | Orange |
| 2 | 4 | Pear |
| 3 | 3 | Orange |

Resources