How to join tables when there are duplicates in right table - sql-server

I have three tables. Table Cust has a custID field, plus various other values (name, address etc)
Table List has a single column ID. Each ID is a custID in the Cust table
Edit: the purpose of this is to filter the records, restricting thge results to ones where the CustID appears in the list table.
All three tables are indexed.
Table Trans has a TransactionID field, a Cust field that holds a customer ID, And other transaction fields
Edit: I should have mentioned that in some cases there will be no transaction record. In this case I want one row of Customer info with the transaction fields null or blank.
I want a query to return cust and transaction ID for each ID in the list table. If there is more than one matching row in the transaction table, I want each included along 3with the matching cust info. So if the tables look like This:
Cust
ID Name
01 John
02 Mary
03 Mike
04 Jane
05 Sue
06 Frank
List
ID
01
03
05
06
Transact
TransID CustId Msg
21 01 There
22 01 is
23 02 a
24 03 tide
25 04 in
26 04 the
27 05 affairs
28 05 of
29 05 men
I want the result set to be:
CustID Name TransID Msg
01 John 21 There
01 John 22 is
03 Mike 24 tide
05 Sue 27 affairs
05 Sue 28 of
05 Sue 29 men
06 Frank -- --
(Where -- represents NULL or BLANK)
Obviously the actual tables are much larger (millions of rows), but that shows the pattern, one row for every item in table Transactions that matches any of the items in the List table, with matching fields from the Cust table. if there is no matching Transaction, one row of customer info from each ID in the List table. CustID is unique in the Cust and List tables, but not in the transaction table.
This needs to work on any version of SQL server from 2005 onward, if that matters.
Any suggestions?

Unless I'm missing something, this is all you need to do:
Select T.CustID, C.Name, T.TransID, T.Msg
From Transact T
Join Cust C On C.Id = T.CustId
Join List L On L.Id = C.Id
Order By T.CustID, T.TransID

;with cust (id, name) as
(
select 1, 'John' union all
select 2, 'Mary' union all
select 3, 'Mike' union all
select 4, 'Jane' union all
select 5, 'Sue'
), list (id) as
(
select 1 union all
select 3 union all
select 5
), transact (TransId, CustId, Msg) as
(
select 21, 1, 'There '
union all select 22, 1, 'is'
union all select 23, 2, 'a'
union all select 24, 3, 'tide'
union all select 25, 4, 'in'
union all select 26, 4, 'the'
union all select 27, 5, 'affairs'
union all select 28, 5, 'of'
union all select 29, 5, 'men'
)
select
CustId = c.id,
Name = c.Name,
TransId = t.TransId,
Msg = t.Msg
from cust c
inner join list l
on c.id = l.id
inner join transact t
on l.id = t.custid
yields:
CustId Name TransId Msg
----------- ---- ----------- -------
1 John 21 There
1 John 22 is
3 Mike 24 tide
5 Sue 27 affairs
5 Sue 28 of
5 Sue 29 men

Related

Select 50 rows, but only up to 10 with same column

We're running SQL Server 2016. I'm currently building a table that will hold employee data for various companies we have dealings with. The table will be populated with employee information for the various companies in the form of:
Name
Company
Status
DateTime
Person 01
Company1
11/08/2021 07:01:00.00
Person 02
Company1
11/08/2021 07:02:00.00
Person 03
Company1
11/08/2021 07:03:00.00
Person 04
Company1
11/08/2021 07:04:00.00
Person 05
Company1
11/08/2021 07:05:00.00
Person 06
Company1
11/08/2021 07:06:00.00
Person 07
Company1
11/08/2021 07:07:00.00
Person 08
Company1
11/08/2021 07:08:00.00
Person 09
Company1
11/08/2021 07:09:00.00
Person 10
Company1
11/08/2021 07:10:00.00
Person 11
Company1
11/08/2021 07:11:00.00
Person 12
Company1
11/08/2021 07:12:00.00
Person 13
Company2
11/08/2021 07:13:00.00
Person 14
Company2
11/08/2021 07:14:00.00
Person 15
Company2
11/08/2021 07:15:00.00
Person 16
Company2
11/08/2021 07:16:00.00
We'll have a lot more data than that, but what we're trying to achieve is, every day, we want to do a selection of the first 50 with a blank status, and group these together.
Once we finish with them for the day, the Status is updated so we can select the next 50 easily.
This part is easy, however, the bit I'm unsure about is that we only want to select up to 10 entries from the same company every day.
For example, we want to retrieve 50 results, but only up to 10 can have the same company.
The 50 results may look like:
10 from Company 1
10 from Company 2
8 from Company 3 (since they didn't submit 8 or they did them later, so their no longer next in line)
2 from Company 4
10 from Company 5
10 from Company 6
I've gotten this far, but I don't know how to only select up to 10 from each company:
select top 50 *
from EmployeeData
where insull(Status,'') <> ''
order by DateTime
Any help would be greatly appreciated.
Thanks
Luke
Something like this maybe:
select top 50 [Name], [Company], [Status], [DateTime]
from (
SELECT [Name], [Company], [Status], [DateTime],
ROW_NUMBER ( ) OVER ( PARTITION BY [Company] order by [Name] ) rownum
FROM EmployeeData
where insull(Status,'') <> ''
) tbl
where rownum <= 10
order by [DateTime]
This query has two CTE's. First, the unique Company names are selected as 'unq_cte'. Second, 10 random rows are selected for each company as 'all_cte'. Last, from the 'all_cte' (collection of maximum 10 rows per company) the query selects 50 rows at random.
;with
unq_cte as (
select distinct Company
from EmployeeData),
all_cte as (
select top10.*
from unq_cte uc
cross apply (select top(10) *
from EmployeeData ed
where uc.Company=ed.Company
order by newid())
top10([Name], Company, [Status], [DateTime]))
select top(50) *
from all_cte
order by newid();

Use join with CTE

I have a table in which i am inserting some records every week. There is a column for Date. I want to compare the data of last week and this week using column key. Below is my table:
Name Date Key
ABC 07 June 1
BAC 07 June 2
WSD 07 June 3
QWE 14 June 9
QWT 14 June 2
DEF 14 June 1
CXZ 14 June 6
I want the data of 14 June in which key is same as in data of 07th june.
Desired output:
Name Date Key
QWT 14 June 2
DEF 14 June 1
I am using CTE to join but i am not getting the desired results.
;WITH T1
AS
(SELECT * FROM [Table] where [Date]= '07 June'),
T2
AS
(SELECT * FROM [Table] where [Date]= '14 June')
SELECT *
FROM T2
INNER JOIN T1 ON T1.[KEY] = T2.[KEY];
What you have should be returning the results you stated that you want. I would maybe simplify this a little bit to a single query with a self join. Something like this.
select t2.*
from [Table] t
join [Table] t2 on t.MyKey = t2.MyKey
where t.MyDate = '07 June'
and t2.MyDate = '14 June'
If you want the data of 14 June in which key is the same as in data of 07th June. You can use intersect:
select
t1.*
from
table
as t1
where
t1.MyDate = '07 June'
intersect select
t2.*
from
table
as t2
where
t2.MyDate = '14 June
You can also think everything dynamic as below. This will return you result regardless what is the date is. this will always compare a row with the row with date 7 day less.
You can check DEMO HERE
SELECT A.*
FROM your_table A
INNER JOIN your_table B
ON A.[Key] = B.[Key] AND DATEADD(DD,7,B.[Date]+ ' 2019') = A.[Date] + ' 2019'
-- Added 2019 To make the string as date

SQL join combined with new column calculation

I have two tables in SQL that look like this:
Table 1:
ID TaxYear Earnings
01 2000 2234
01 2001 123
02 2004 12344
02 2006 234
02 2007 0
02 2008 123
Table 2:
ID JobEnd
01 1998
02 2000
02 2007
I need to combine these tables to make a new column giving the number of years between TaxYear and JobEnd. However, I need this value to reset every time TaxYear passes a new JobEnd year. So my final table would look like this:
ID TaxYear Earnings YearsSinceJobEnd
01 2000 2234 2
01 2001 123 3
02 2004 12344 4
02 2006 234 6
02 2007 0 7
02 2008 123 1
For ID 02, when YearsSinceJobEnd is calculated as TaxYear minus 2000, up until TaxYear passes the new JobEnd year of 2007, when it is subsequently calculated as TaxYear minus 2007.
I'm getting very confused about how to do this. If I join the tables I end up with multiple columns per TaxYear, which I need to avoid. But I can't think how to calculate the new column without joining them.
Any help would be much appreciated.
You can use datediff() with DATETIMEFROMPARTS ():
select t1.id, t1.TaxYear, t1.Earnings,
datediff(year, DATEFROMPARTS(t2.JobEnd, 1, 1), DATEFROMPARTS(t1.TaxYear, 1, 1)) as YearsSinceJobEnd
from t1 inner join
t2
on t2.id = t1.id;
If you don't want JOIN then use APPLY :
select t1.id, t1.TaxYear, t1.Earnings,
datediff(year, DATEFROMPARTS(t2.JobEnd, 1, 1), DATEFROMPARTS(t1.TaxYear, 1, 1)) as YearsSinceJobEnd
from t1 cross apply
( select top (1) t2.JobEnd
from t2
where t2.id = t1.id and t2.JobEnd < t1.TaxYear
order by t2.JobEnd desc
) t2;
you can user CROSS APPLY to find the required JobEnd for each ID
SELECT t1.ID, t1.TaxYear, t1.Earnings,
YearsSinceJobEnd = t1.TaxYear - e.JobEnd
FROM Table1 t1
CROSS APPLY
(
SELECT JobEnd = MAX(t2.JobEnd)
FROM Table2 t2
WHERE t2.ID = t1.ID
AND t2.JobEnd < t1.TaxYear
) e

SQL Return datas from the second table based on the data of the first table

I have two SQL Server tables that are not linked together (No join) and I want to get the data from the second table based on the data of the first table. In the first table I have this:
Table 1
id name
----------
4 BOX-A
8 PART-D
Table 2
id name
------------
14 BOX-A1
25 BOX-A2
38 TOOL-A1
39 TOOL-A2
40 PART-D1
41 PART-D2
What I want to do is that for each name found in table 1, I want to return all the matches in the table 2, so at the end I will have something like this:
id name
-----------
14 BOX-A1
25 BOX-A2
40 PART-D1
41 PART-D2
You can use join or exists:
select t2.*
from table2 t2
where exists (select 1 from table1 t1 where t2.name like concat(t1.name, '%'));

Getting sub products from same table

I have two tables, table1 has end products and product parts, table2 has a mapping between table1.productid and partsid. i.e.
table1
--------------------------------------------
productid descrip code cost ....etc
1235 product A 07 12.5 ......
789 labor 03 2.5 ....
839 part1 03 5 ....
and table2 looks like
table2
--------------------------------------------
productid partsID quantity
1235 789 1
1235 839 2
2341 2315 2
.....
I need to select the end product that meets specific code and go pull up the parts from the part table and display like below:
Resuls
--------------------------------------------
productid descrip code partsID cost ....etc
1235 product A 07 789 2.5 ......
1235 product A 03 839 5 ....
.......
Basically for each end product, line up the parts to the right and cost and all other details associated with the parts and not the end product.
Appreciate any help with this.
Could you not join the Two tables across ProductID field:
SELECT * FROM
dbo.TABLE1 o INNER JOIN
dbo.TABLE2 t ON o.ProductId = t.ProductId
WHERE o.code = 7
OR o.code = 3
etc ... whatever additional criteria you need
If you also need to Join the partsID you could say
AND o.PartId = t.partsId
It looks like you want to get something like this query, but I'm not sure about the code=07, you've specified in the example. Probably the code must be 03 for both parts.
SELECT
prd.productid,
prd.descrip,
prt.code,
prdprt.partsID,
prt.cost,
....etc
FROM table1 prd
JOIN table2 prdprt ON prd.productid=prdprt.productid
JOIN table1 prt ON prdprt.partsID=prt.productid
WHERE prd.code in ('07', ...othecodes) --if you filter by product's code
OR prt.code in ('03', ...othecodes) --if you filter by parts's code

Resources