Limiting Join Qty - sql

If I have a couple of tables like the following
Table1
-----------------------------
Name Qty
-----------------------------
John 1
Paul 2
...
Ringo 1
and
Table2
-----------------------------
Forename Surname Cost
-----------------------------
John Smith 123
John Jones 815
Paul Smith 273
Paul Jones 297
...
Ringo Smith 755
Ringo Jones 334
and I want to construct a query so the quantity returned from Table2 by each subset is ordered by Table2.Cost and limited by Table1.Qty, to return something like:
Results
-----------------------------
Forename Surname Cost
-----------------------------
John Jones 815
Paul Jones 297
Paul Smith 273
Ringo Smith 755
Is there a way to do this?

Try this one
SELECT T.Forename,T.Surname, T.Cost
FROM
(
SELECT *,
ROW_NUMBER() OVER ( PARTITION BY Forename ORDER BY Cost DESC )
AS rn
FROM Table2
) T
JOIN Table1 ON Table2.Foreman=Table1.Name
WHERE T.rn <=Qty;

SELECT Forename,
Surname,
Cost
FROM
(
SELECT *,
ROW_NUMBER() OVER ( PARTITION BY Forename ORDER BY Cost DESC )
AS rn
FROM Table2
)
WHERE rn = 1;

create table #table1(name varchar(100), qty int)
create table #table2 (forename varchar(100), surname varchar(100), cost int)
insert into #table1 values
('John',1),
('Paul',2),
('Ringo',1)
insert into #table2 values
('John' , 'Smith' , 123),
('John' , 'Jones' , 815 ) ,
('Paul' , 'Smith' , 273),
('Paul' , 'Jones' , 297),
('Ringo' , 'Smith' , 755),
('Ringo' , 'Jones' , 334)
select * from
#table1 t1 cross apply
(select top (t1.qty) * from #table2 t2 where t1.name = t2.forename order by t2.cost desc) t

Updated and Tested: If it's about getting the highest cost from Table 2 depending upon Qty from Table 1, then use the following:
CREATE TABLE #table1 (name NVARCHAR(100), qty INT);
CREATE TABLE #table2 (forename NVARCHAR(100), surname NVARCHAR(100), cost INT);
INSERT INTO #table1 VALUES
('John', 1),
('Paul', 2),
('Ringo', 1);
INSERT INTO #table2 VALUES
('John', 'Smith', 123),
('John', 'Jones', 815 ),
('Paul', 'Smith', 273),
('Paul', 'Jones', 297),
('Ringo', 'Smith', 755),
('Ringo', 'Jones', 334);
WITH DATA AS (
SELECT t2.forename, t2.surname, t2.cost, t1.qty,
rn = ROW_NUMBER() OVER (PARTITION BY t1.name ORDER BY t2.cost DESC)
FROM
#table1 t1
INNER JOIN #table2 t2 ON t1.name = t2.forename
)
SELECT d.forename, d.surname, d.cost
FROM
DATA d
WHERE d.rn <= d.qty

Related

excluding dups which are lower than max values in SQL

I have the following simple table (Table1), where each row is a student_ID and their name, and each student has one or multiple wins (Wins). I would like to output: Student_ID, Student_name, count of Wins, sorted by count of Wins (descending) and then Student_ID (ascending), excluding those students who have the same count of Wins which is less than the max of the Wins (i.e.5). In other words, Lizzy and Mark have the same count of wins, and 3 is lower than 5, so the output will exclude the two students, Lizzy and Mark.
From comments: "Betty, David and Cathy should be excluded", also.
Table1:
student_id
student_name
wins
1
John
YES
1
John
YES
1
John
YES
1
John
YES
1
John
YES
2
Brandon
YES
2
Brandon
YES
2
Brandon
YES
2
Brandon
YES
2
Brandon
YES
3
Lizzy
YES
3
Lizzy
YES
3
Lizzy
YES
4
Mark
YES
4
Mark
YES
4
Mark
YES
5
Betty
YES
6
David
YES
7
Cathy
YES
8
Joe
YES
8
Joe
YES
Desired output:
student_id
student_name
cnt_wins
1
John
5
2
Brandon
5
8
Joe
2
Here is my SQL in Oracle. I can't figure out what went wrong. The log says "(SELECT b.cnt_wins, count(b.student_id) has too many values".
WITH st_cte AS
(SELECT student_id, student_name, count(wins) cnt_wins
FROM Table1
GROUP BY student_id, student_name
ORDER BY count(wins) DESC, student_id)
SELECT *
FROM st_cte a
WHERE a.cnt_wins not in
(SELECT b.cnt_wins, count(b.student_id)
FROM st_cte b
WHERE b.cnt_wins <
(SELECT max(c.cnt_wins) FROM st_cte c)
GROUP BY b.cnt_wins
HAVING count(b.student_id) > 1);
There are too many values selected inside the 'in' select:
WHERE a.cnt_wins -- 1 value
not in
(SELECT b.cnt_wins, count(b.student_id) -- 2 values
FROM st_cte b
you shoud either do :
WHERE a.cnt_wins not in
(SELECT b.cnt_wins
FROM st_cte ...
or
WHERE (a.cnt_wins, count(something)) not in
(SELECT b.cnt_wins, count(b.student_id)
FROM st_cte ...
Updated based on updated requirements...
The requirement was ambiguous in that Betty, David, and Cathy seem to also meet the criteria to be removed from the result. This requirement was clarified and those rows should have been removed.
Logic has been added to allow only all max_cnt rows, plus any students with a unique count.
Also note that if wins can be any other non-null value, COUNT(wins) is not correct.
Given all that, maybe something like this is a starting point:
Fiddle
WITH cte AS (
SELECT student_id, student_name
, COUNT(wins) cnt_wins
, MAX(COUNT(wins)) OVER () AS max_cnt
FROM Table1
GROUP BY student_id, student_name
)
, cte2 AS (
SELECT cte.*
, COUNT(*) OVER (PARTITION BY cnt_wins) AS cnt_students
FROM cte
)
SELECT student_id, student_name, cnt_wins
FROM cte2
WHERE max_cnt = cnt_wins
OR cnt_students = 1
ORDER BY cnt_wins DESC, student_id
;
and to handle wins that can be other non-null values:
WITH cte AS (
SELECT student_id, student_name
, COUNT(CASE WHEN wins = 'YES' THEN 1 END) cnt_wins
, MAX(COUNT(CASE WHEN wins = 'YES' THEN 1 END)) OVER () AS max_cnt
FROM Table1
GROUP BY student_id, student_name
)
, cte2 AS (
SELECT cte.*
, COUNT(*) OVER (PARTITION BY cnt_wins) AS cnt_students
FROM cte
)
SELECT student_id, student_name, cnt_wins
FROM cte2
WHERE max_cnt = cnt_wins
OR cnt_students = 1
ORDER BY cnt_wins DESC, student_id
;
Result (with data to test the new requirement, one student (Joe) with unique counts (2)):
STUDENT_ID
STUDENT_NAME
CNT_WINS
1
John
5
2
Brandon
5
8
Joe
2
Setup:
CREATE TABLE table1 (
Student_ID int
, Student_Name VARCHAR2(20)
, Wins VARCHAR2(10)
);
BEGIN
-- Assume only wins are stored.
INSERT INTO table1 VALUES ( 1, 'John', 'YES');
INSERT INTO table1 VALUES ( 1, 'John', 'YES');
INSERT INTO table1 VALUES ( 1, 'John', 'YES');
INSERT INTO table1 VALUES ( 1, 'John', 'YES');
INSERT INTO table1 VALUES ( 1, 'John', 'YES');
INSERT INTO table1 VALUES ( 2, 'Brandon', 'YES');
INSERT INTO table1 VALUES ( 2, 'Brandon', 'YES');
INSERT INTO table1 VALUES ( 2, 'Brandon', 'YES');
INSERT INTO table1 VALUES ( 2, 'Brandon', 'YES');
INSERT INTO table1 VALUES ( 2, 'Brandon', 'YES');
INSERT INTO table1 VALUES ( 3, 'Lizzy', 'YES');
INSERT INTO table1 VALUES ( 3, 'Lizzy', 'YES');
INSERT INTO table1 VALUES ( 3, 'Lizzy', 'YES');
INSERT INTO table1 VALUES ( 4, 'Mark', 'YES');
INSERT INTO table1 VALUES ( 4, 'Mark', 'YES');
INSERT INTO table1 VALUES ( 4, 'Mark', 'YES');
INSERT INTO table1 VALUES ( 5, 'Betty', 'YES');
INSERT INTO table1 VALUES ( 6, 'David', 'YES');
INSERT INTO table1 VALUES ( 7, 'Cathy', 'YES');
INSERT INTO table1 VALUES ( 8, 'Joe', 'YES');
INSERT INTO table1 VALUES ( 8, 'Joe', 'YES');
END;
/
Correction to the original query in the question:
WITH st_cte AS
(SELECT student_id, student_name, count(wins) cnt_wins
FROM Table1
GROUP BY student_id, student_name
ORDER BY count(wins) DESC, student_id
)
SELECT *
FROM st_cte a
WHERE a.cnt_wins not in
(SELECT b.cnt_wins
FROM st_cte b
WHERE b.cnt_wins < (SELECT max(c.cnt_wins) FROM st_cte c)
GROUP BY b.cnt_wins
HAVING count(b.student_id) > 1
)
;

Put value in minimum ID record in SQL data set

I have a table where I'm trying to get the following result: CategoryType should be set to 0 for the member record with the lowest categoryID and 1 if that ID is NULL. CategoryType is not a field from the table so it should be added via a Case statement. I wrote a script using the Row_Number() window function to organize the records but I'm not sure how to put a 0 for the lowest record. I tried using a Min(Case) statement but that didn't work.
declare #t table(memberid int, lastname varchar(50), firstname varchar(50), categoryid int)
insert into #t
values(1, 'Jones', 'Tom', 2), (1, 'Jones', 'Tom', 4),
(2, 'Hanson', 'Ron', 3), (2, 'Hanson', 'Ron', 4),
(2, 'Hanson', 'Ron', 5),
(3, 'Smith', 'Jack', NULL);
This is the result I'm trying to get:
MemberID LastName FirstName CategoryID CategoryType
1 Jones Tom 2 0
1 Jones Tom 4 NULL
2 Hanson Ron 3 0
2 Hanson Ron 4 NULL
2 Hanson Ron 5 NULL
3 Smith Jack NULL 1
Or this:
select *
,case
when categoryid is null then 1
when FIRST_VALUE(categoryid) over (partition by memberid order by categoryid) = categoryid then 0 else null
end as x
from #t
I think you want this
SELECT *,CASE WHEN categoryid IS NULL THEN 1
WHEN ROW_NUMBER() OVER (PARTITION BY categoryid ORDER BY memberid) = 1
THEN 0
END CategoryType FROM #T
Easier to read/extend (if needed):
SELECT a.MemberID,a.LastName,a.FirstName,a.CategoryID
,CASE
WHEN a.rn = 1 AND a.CategoryID IS NULL THEN 1
WHEN a.rn = 1 THEN 0
ELSE NULL
END AS [CategoryType]
FROM (
SELECT t.MemberID,t.LastName,t.FirstName,t.CategoryID
,ROW_NUMBER()OVER(PARTITION BY t.MemberID ORDER BY t.CategoryID) AS [rn]
FROM #t t
) a
;
I think you want:
select t.*,
(case when categoryid is null
then 1
when row_number() over (partition by memberid order by categoryid) = 1
then 0
end) as categorytype
from #t t;
Here is a db<>fiddle.
Hope Can Help You
declare #t table(memberid int,
lastname varchar(50),
firstname varchar(50),
categoryid int)
insert into #t
values(1, 'Jones', 'Tom', 2),
(1, 'Jones', 'Tom', 4),
(2, 'Hanson', 'Ron', 3),
(2, 'Hanson', 'Ron', 4),
(2, 'Hanson', 'Ron', 5),
(3, 'Smith', 'Jack', NULL)
Select
A.*,
Case When a.categoryid is null Then '1'
When b.categoryid is not null Then '0' Else null End CategoryType
From #t A
Left Join (
Select memberid, min(categoryid) categoryid From #t
Group By memberid
) B On B.memberid = A.memberid and B.categoryid = A.categoryid

How to get row values in to columns in sql server with based on id

I have the following table:
id Prefix FisrtName LastName
--------------------------------------------
123 Mr Lynn Berg
123 Ms Madeline Owen
123 Mrs Zelenia Sellers
101 Mrs Jesse Vincent
101 Mr Chaim Long
The result table should look like this
id name1 name2 name2
-----------------------------------------------------------
123 Mr Lynn Berg Ms MadelineOwen Mrs Zelenia Sellers
101 Mrs Jesse Vincent Mr Chaim Long
How could I achieve this result in SQL Server? Can I use pivot function?
Please help
You can also do conditional aggregation :
select id, max(case when seq = 1 then Name end) as Name1,
max(case when seq = 2 then Name end) as Name2,
max(case when seq = 3 then Name end) as Name3
from (select id, concat(Prefix,' ',FisrtName,' ',LastName) as Name,
row_number() over (partition by id order by (select null)) as seq
from table
) t
group by id;
Try this simple pivot:
declare #tbl table (id int, Prefix varchar(3), FirstName varchar(20), LastName varchar(20));
insert into #tbl values
(123, 'Mr', 'Lynn', 'Berg'),
(123, 'Ms', 'Madeline', 'Owen'),
(123, 'Mrs', 'Zelenia', 'Sellers'),
(101, 'Mrs', 'Jesse', 'Vincent'),
(101, 'Mr', 'Chaim', 'Long');
select id, [1] [Name1], [2] [Name2], [3] [Name3] from (
select id,
ROW_NUMBER() over (partition by id order by (select null)) rn,
Prefix + ' ' + FirstName + ' ' + LastName [FullName]
from #tbl
) a pivot (
max(fullname) for rn in ([1],[2],[3])
) b;
CREATE TABLE #Table1 (
id INT
,Prefix VARCHAR(3)
,FisrtName VARCHAR(8)
,LastName VARCHAR(7)
);
INSERT INTO #Table1
VALUES (
123
,'Mr'
,'Lynn'
,'Berg'
)
,(
123
,'Ms'
,'Madeline'
,'Owen'
)
,(
123
,'Mrs'
,'Zelenia'
,'Sellers'
)
,(
101
,'Mrs'
,'Jesse'
,'Vincent'
)
,(
101
,'Mr'
,'Chaim'
,'Long'
)
SELECT *
FROM #Table1
SELECT id
,[1] [Name1]
,[2] [Name2]
,[3] [Name3]
FROM (
SELECT id
,CONCAT (
PREFIX
,FISRTNAME
,LASTNAME
) AS Namm
,ROW_NUMBER() OVER (
PARTITION BY id ORDER BY (
id
)
) AS rn
FROM #TABLE1
) a
pivot(max(Namm) FOR rn IN (
[1]
,[2]
,[3]
)) b

Delete duplicate rows from a table and referenced table microsoft sql server

I have table Person :
PersonId | FirstName | LastName |
1 | 'John' | 'Doe' |
2 | 'Mike' | 'Test' |
3 | 'John' | 'Doe' |
4 | 'Mike' | 'Test' |
5 | 'John' | 'Doe' |
6 | 'John' | 'Doe' |
Table Customer :
CustomerId | PersonId |
1001 | 1 |
1002 | 2 |
1003 | 3 |
1004 | 4 |
1005 | 5 |
1006 | 6 |
I want to delete Customer 1003,1004,1005,1006 because their Persons are duplicate, but PersonId is not same.
This should check FirstName and LastName in Person table and delete the duplicates in Customer table , Then delete duplicates in Person table. ( 3,4,5,6 )
Sorry if similar questions has been asked before but I couldn't do this.
Check This.
We delete first from Customer table. First we find duplicate records by using Row_number() and deleting personid which have to rank more than 1.
Below Query show duplicate records :
select ROW_NUMBER () over ( partition by firstname,lastname order by
PersonId ) RID, PersonId,FirstName,LastName
from #Person
After finding Duplicates we delete it from customer table then Person.
delete from Customer where PersonId in
(
select distinct PersonId P from
( select ROW_NUMBER () over ( partition by firstname,lastname order by PersonId ) RID, PersonId,FirstName,LastName from #Person )a
where RID>1
)
delete from Person where PersonId in
(
select distinct PersonId P from
( select ROW_NUMBER () over ( partition by firstname,lastname order by PersonId ) RID, PersonId,FirstName,LastName from #Person )a
where RID>1
)
Use this query to view the duplicates:
with duplicatecte(personid,rownum)As
(
select personid ,
row_Number() over(partition by FirstName+LastName order by personid)
from #person
)
select b.personid,customerid from duplicatecte a
inner join #customer b on a.personid=b.personid where rownum>1
Modify this query to delete as below
with duplicatecte(personid,rownum)As
(
select personid ,
row_Number() over(partition by FirstName+LastName order by personid)
from #person
)
delete b
from duplicatecte a
inner join #customer b on a.personid=b.personid where rownum>1
Begin Tran
CREATE TABLE #Person(PersonId INT,FirstName NVARCHAR(50),LastName NVARCHAR(50))
CREATE TABLE #Customer (CustomerId INT,PersonId INT)
INSERT INTO #Person
SELECT 1,'John','Doe' UNION ALL
SELECT 2 ,'Mike','Test' UNION ALL
SELECT 3 ,'John','Doe' UNION ALL
SELECT 4 ,'Mike','Test' UNION ALL
SELECT 5 ,'John','Doe' UNION ALL
SELECT 6 ,'John','Doe'
INSERT INTO #Customer
SELECT 1001, 1 UNION ALL
SELECT 1002 ,2 UNION ALL
SELECT 1003 ,3 UNION ALL
SELECT 1004 ,4 UNION ALL
SELECT 1005,5 UNION ALL
SELECT 1006,6
GO
WITH CTE (PersonId, DuplicateCount)
AS
(
SELECT FirstName,
ROW_NUMBER() OVER(PARTITION BY FirstName,LastName ORDER BY FirstName,PersonId) AS DuplicateCount
FROM #Person
)
--Select * from CTE WHERE DuplicateCount>1
DELETE FROM CTE WHERE DuplicateCount >1
DELETE FROM #Customer WHERE PersonId NOT IN(SELECT PersonId FROM #Person)
Select * from #Person
SELECT * from #Customer
ROLLBACK TRAN
declare #tbl table
(pid int
)
;with cte
as
(
select t1.*,row_number() over (partition by firstname,lastname order by personid) as rownum
from
person t1
)
delete
from
cte
output deleted.personid into #tbl where rownum>1
delete from customer where personid in (select personid from #tbl)
This will work for you:
Declare #person As table
(
PersonId int,
FirstName varchar(25),
LastName varchar(25)
)
Declare #customer As table
(
CustomerId int,
PersonId int
)
Insert Into #person (PersonId,FirstName,LastName) values(1,'John','Doe')
Insert Into #person (PersonId,FirstName,LastName) values(2,'Mike','Test')
Insert Into #person (PersonId,FirstName,LastName) values(3,'John','Doe')
Insert Into #person (PersonId,FirstName,LastName) values(4,'Mike','Test')
Insert Into #person (PersonId,FirstName,LastName) values(5,'John','Doe')
Insert Into #person (PersonId,FirstName,LastName) values(6,'John','Doe')
Insert Into #customer(CustomerId,PersonId) values(1001,1)
Insert Into #customer(CustomerId,PersonId) values(1002,2)
Insert Into #customer(CustomerId,PersonId) values(1003,3)
Insert Into #customer(CustomerId,PersonId) values(1004,4)
Insert Into #customer(CustomerId,PersonId) values(1005,5)
Insert Into #customer(CustomerId,PersonId) values(1006,6)
select p.PersonId into #temp from #person p right join
(Select PersonId,FirstName,LastName, ROW_NUMBER() over (partition by FirstName,LastName Order by PersonId) rownumber
from #person ) a
on p.PersonId=a.PersonId where a.rownumber>1
delete from #customer where PersonId in (select PersonId from #temp)
delete from #person where PersonId in (select PersonId from #temp)
select *from #customer
select *from #person

SQL Server Group Concat with Different characters

I have looked through a number of solutions to emulating "Group concat" functionality in SQL Server. I wanted to make a more human readable solution though and I can't work out how to do it.
I have a view:
ParentID | ChildName
Which contains the records, for example:
1 | Max
1 | Jessie
2 | Steven
2 | Lucy
2 | Jake
3 | Mark
I want to "Group Concat" these to get:
1 | Max and Jessie
2 | Steven, Lucy and Jake
3 | Mark
So If there is only 1 child, just return name, if there are more than one, concat the last 2 with an ' and ' and all others with a ', '.
I am a bit stuck on how to do this without resorting to CLR, which I don't want to do. I am happy with a function - but speed is an issue and how do I determine the child number so I can choose between ' and ', ', ' or ''?
make a more human readable solution
Sorry, this is the best I can do with your requirement.
SQL Fiddle
MS SQL Server 2008 Schema Setup:
create table YourTable
(
ParentID int,
ChildName varchar(10)
);
insert into YourTable values
(1, 'Max'),
(1, 'Jessie'),
(2, 'Steven'),
(2, 'Lucy'),
(2, 'Jake'),
(3, 'Mark');
Query 1:
with T as
(
select ParentID,
ChildName,
row_number() over(partition by ParentID order by ChildName) as rn,
count(*) over(partition by ParentID) as cc
from YourTable
)
select T1.ParentID,
(
select case
when T2.rn = 1 and T2.cc > 1 then ' and '
else ', '
end + T2.ChildName
from T as T2
where T1.ParentID = T2.ParentID
order by T2.rn desc
for xml path(''), type
).value('substring(text()[1], 3)', 'varchar(max)') as ChildNames
from T as T1
group by T1.ParentID
Results:
| PARENTID | CHILDNAMES |
------------------------------------
| 1 | Max and Jessie |
| 2 | Steven, Lucy and Jake |
| 3 | Mark |
select ParentID,STUFF((SELECT ' and '+ChildName
FROM Table1 where ParentID=a.ParentID
FOR XML PATH('')),1,4,'') as cnmae from Table1 a
group by ParentID
SQL FIDDLE DEMO
Good logical question. Please check the query below (bit lengthy, but could not stop myself posting my small logic :)).
CREATE TABLE #SampleTable ([ParentID] int, [ChildName] varchar(6));
INSERT INTO #SampleTable VALUES (1, 'Max')
INSERT INTO #SampleTable VALUES (1, 'Jessie')
INSERT INTO #SampleTable VALUES (2, 'Steven')
INSERT INTO #SampleTable VALUES (2, 'Lucy')
INSERT INTO #SampleTable VALUES (2, 'Jake')
INSERT INTO #SampleTable VALUES (3, 'Mark')
select * From #SampleTable
;WITH T(xParentID, xChildName, xChildNameResult, xC1, xC2)AS
(
SELECT * FROM(
SELECT
ParentID ,
ChildName,
CAST(ChildName AS NVARCHAR(MAX)) AS ChildNameResult,
ROW_NUMBER() OVER (PARTITION BY [ParentID] ORDER BY ChildName) C1,
COUNT(*) OVER (PARTITION BY [ParentID]) C2
FROM #SampleTable)x WHERE x.C1=1
UNION ALL
SELECT ParentID, ChildName,
CAST(T.xChildNameResult+(CASE WHEN C1=1 THEN '' WHEN C1=C2 THEN ' and ' ELSE ', ' END)+ChildName AS NVARCHAR(MAX)), C1, C2
FROM
(
SELECT
ParentID ,
ChildName,
ROW_NUMBER() OVER (PARTITION BY ParentID order by ChildName) C1,
COUNT(*) OVER (PARTITION BY ParentID) C2
FROM #SampleTable
)y INNER JOIN T ON y.ParentID=T.xParentID and y.c1=T.xC1+1
)SELECT xParentID, xChildNameResult FROM T where xC1=xC2
OPTION (MAXRECURSION 0);