SQL: Combining three tables using JOIN and UNION - sql-server

In a rental management database I have three tables, one containing my article stock, one for the bookings of those articles and one storing all projects:
stock
IdStockType Number Caption Inventory
1 100001 Monitor 10
2 100002 Keyboard 25
3 100003 Notebook 05
4 100004 Desktop 07
bookings
id IdStockType RentalPrice Factor Project
1 2 15 5 1
2 2 03 2 2
3 4 90 3 2
projects
id Caption startDate endDate
1 Festival 2019 2019-03-01 2019-03-05
2 Kick-Off ABC 2019-04-15 2019-04-17
3 Big Conference 2019-04-16 2019-04-16
Now I want to write a query giving me the whole stock with information about bookings and project. If an article was not rented, it should be listed anyway. If it was rented, it should only be listed with the booking & project information. So it can be there multiple times:
IdStockType Number Factor Inventory Article RentalPrice Project
1 100001 (NULL) 10 Monitor (NULL) (NULL)
2 100002 5 25 Keyboard 15 Festival 2019
2 100002 2 25 Keyboard 03 Kick-Off ABC
3 100003 (NULL) 05 Notebook (NULL) (NULL)
4 100004 3 07 Desktop 90 Kick-Off ABC
Now I have problem with my query:
SELECT * FROM (
SELECT
p.IdStockType,
p.Number,
o.Factor,
p.Inventory,
p.Caption AS Article,
q.Caption AS Project
FROM
stock p,
bookings o,
projects q
WHERE
p.IdStockType = o.IdStockType AND
o.Project = q.id
) AS u
RIGHT JOIN (SELECT
r.IdStockType,
r.Number,
NULL,
NULL,
r.Caption AS Article,
NULL
FROM
stock r) d ON d.IdStockType = u.IdStockType;
The resulting amount of outputted lines seems to be correct, but the combination of the information is weird because the concordant columns are added as new ones.
Can anybody help me with the query?
Thanks in advance.
Regards
Lars

select *
from Stock stock
left join (
select
*
from
Bookings bookings
join Projects projects
on bookings.Project = projects.Id
) bookingProjects on stock.IdStockType = bookingProjects.IdStockType

Related

Linking tables on date basis

I am having two tables as below
Child_Attendance
childid updatedon presentdays
1 31/01/2018 20
1 28/02/2018 15
1 31/03/2018 18
1 30/04/2018 24
1 31/05/2018 17
1 30/06/2018 19
2 31/03/2018 25
2 30/04/2018 28
2 31/05/2018 22
2 30/06/2018 23
And the Second table as
childid class admissiondate
1 creches 15/06/2017
1 balwari 01/02/2018
2 creches 01/01/2017
2 balwari 01/01/2018
2 Bridge Course 01/04/2018
Now, I need a query to return childid,updatedon,presentdays,class.
I am new to sql and don't have any idea how to do it.
I have tried
SELECT t1.childid,t1.updatedon,t1.presentdays,t2.class
FROM child_attendance t1 LEFT JOIN class_allocation t2
ON t1.childid = t2.childid
AND t1.updatedon >= t2.admissiondate
My output should be like this
You can see the child 1 was admitted in creche on dated 15/06/2017 and balwari on dated 01/02/2018. This means he was in creche from 15/06/2018 till 01/02/2018.
I think this query could get what you want:
SELECT t1.childid, t1.updatedon, t1.presentdays,
class=(SELECT TOP (1) class
FROM Class_Allocation
WHERE childid=t1.childid
AND t1.updatedon>=admissiondate
ORDER BY admissiondate DESC)
FROM Child_Attendance t1

Merge with a preferred source

I'm using SSIS 2008 R2 for a few weeks now and I'm stuck at this moment.
I need to merge two tables from two different SQL Servers into a new table.
This is what I try to achieve:
Table 1:
Name Price Quantity
------------------------
Item 1 23 5
Item 2 50 2
Item 3 30 10
Table 2:
ID Name Price Quantity
------------------------------
101 Item 1 60 7
203 Item 3 80 15
Result:
ID Name Price Quantity
-------------------------------------
101 Item 1 60 7
NULL Item 2 50 2
203 Item 3 80 15
I've tried to use merge but that duplicates Item 1 and Item 3.
I'm trying to create it within Development Studio so there is no real code that I can show.
This is what I've created
Select T2.ID, T1.Name,Price=case when isnull(T1.Price,0)>isnull(T2.Price,0) then
isnull(T1.Price,0) else isnull(T2.Price,0) end,
Quantity=case when isnull(T1.Quantity,0)>isnull(T2.Quantity,0)
then isnull(T1.Quantity,0) else isnull(T2.Quantity,0) end
from Table1 T1
left outer join Table2 T2 on T1.Name=T2.Name

Custom ordering of records in SQL Server

I have a table such as http://sqlfiddle.com/#!6/e4f6f which contains records that need to be reported on an Excel using SSIS.
However, the ordering of records needs to be custom.
Such as below
AID BID CID CurrencyID ClassID Year MetricID Value ReferenceID
220 1 3 6 1147 2012 C1 653465.751842658967 V001
220 1 3 6 1147 2012 C2 0.000000000000 V001
220 1 3 6 1156 2012 C1 1151019.50078003120 V001
220 1 3 6 1156 2012 C2 0.000000000000 V001
As you can see the records are grouped such that they are ordered on ReferenceID first and then all the other dimension keys except the MetricID. Any help is much appreciated
Put MetricID as the last column in your ORDER BY:
SELECT *
FROM [FactValidationResult]
ORDER BY
ReferenceID,
AID,
BID,
CID,
CurrencyID,
Year,
ClassID,
MetricID

SELECT query that Selects from Next Row given certain criteria using SQL Server 2008 R2?

I want to create a table that shows employees' hire and accompanying term date on the same line. I'm using data in 2 tables: employees and EEChange.
EEChange has three columns that are important: employee_no, action_date, action_type. The action_type will only be 'H', 'R' or 'T' for 'Hire', 'Rehire' or 'Term'. As far as I'm concerned the 'Hire' and 'Rehire' dates are effective dates and the 'Term' dates are termination dates.
I can create a select query that places the effective dates (ED) in the ED column and the term dates (TD) in the TD column. The problem is they are not on the same row. Another difficulty I'm having is that there may not be an associated TD for every ED. See below: Employee (EE) 14 was Hired on 5/1/98, rehired on 9/13/06 and termed on 3/23/98. I do not know why my database has a Rehire followed by a Hire. A far as I am concerned EE 14 was hired on 5/1/98 and termed on 3/23/98. I am going to assume there was no break in employment.
Here are my tables:
employees:
EE No Name
6 Anil
12 Viktor
14 Sherry
15 Juan
48 Susan
50 Kevin
EEChange:
EE No Action_type Action_date
6 H 5/1/1998
6 T 7/26/2010
12 H 5/1/1998
12 R 4/16/2012
14 H 5/1/1998
14 R 9/13/2006
14 T 9/19/2008
15 H 3/23/1998
48 H 7/1/1998
48 R 10/21/2008
48 T 1/3/2009
50 H 7/2/1998
50 R 7/16/2010
50 T 5/1/2012
50 R 12/1/2013
50 T 2/15/2015
My query:
SELECT
LTRIM(employees.employee_no) as [EE No],
row_number() OVER (PARTITION BY employees.employee_no ORDER BY EEChange.action_date) AS [Seq],
EEChange.action_type,
CASE
WHEN EEChange.action_type = 'H' OR EEChange.action_type = 'R'
THEN EEChange.action_date
END AS [Effective Date],
CASE
WHEN EEChange.action_type = 'T'
THEN EEChange.action_date
END AS [Term Date]
FROM
dbo.employees employees
LEFT OUTER JOIN
dbo.emp_employment_action_changes EEchange ON employees.employee_no = EEChange.employee_no
I added the Sequence column to see if I could order the action_date column per [EE No]. I was able to do so, but ultimately unsuccessful and finding a select query that would give me the desired result: ED and TD on the same line.
My result:
EE No Seq action_type Effective Date Term Date
6 1 H 5/1/1998
6 2 T 7/26/2010
12 1 H 5/1/1998
12 2 R 4/16/2012
14 1 H 5/1/1998
14 2 R 9/13/2006
14 3 T 9/19/2008
15 1 H 3/23/1998
48 1 H 7/1/1998
48 2 R 10/21/2008
48 3 T 1/3/2009
50 1 H 7/2/1998
50 2 R 7/16/2010
50 3 T 5/1/2012
50 4 R 12/1/2013
50 5 T 2/15/2015
Desired result:
EE No Name Effective Date Term Date
6 Anil 5/1/1998 7/26/2010
12 Viktor 5/1/1998
14 Sherry 5/1/1998 9/19/2008
15 Juan 3/23/1998
48 Susan 7/1/1998 1/3/2009
50 Kevin 7/2/1998 5/1/2012
50 Kevin 12/1/2013 2/15/2015
Notice how Kevin has 5 lines in the EEChange table. I only want the first ED its accompanying Term date and the next ED and accompanying Term date. For active EEs there will be no final term date.
Any help anyone can give would be great. I tried using LEAD() but it doesn't work with SQL Server 2008 R2. I couldn't find a way to make LEAD() skip over EDs that came in the sequence before TDs.
So, it looks like your problem really is a gaps-and-island problem, meaning that you need to find the start and end of discrete ranges. If we treat both actions H and R as markers for the start of a range the problem will be to find the earliest end date succeeding every start date that is later than the date. (Sort of - I find it hard to describe). The logic should be pretty easy to follow though.
I tested with your sample data and this query seems to give the desired result.
with
a as (
select
e.EENo, e.Name,
case
when Action_type IN ('R','H')
then Action_date
end Effective_date,
case
when Action_type = 'T'
then Action_date
end Term_date
from employees e
join EEChange ee on e.EENo = ee.EENo
),
b as (
select
EENo, Name, Effective_date,
case
when Effective_date is not null
then (
select min(term_date)
from a a2
where a2.Term_date > a.Effective_date and a2.EENo = a.EENo
)
end Term_date
from a
)
select
EENo, Name, min(Effective_date) Effective_date, Term_date
from b
where Effective_date is not null
group by EENo, Name, Term_date
Sample SQL Fiddle
Sample result:
| EENo | Name | Effective_date | Term_date |
|------|--------|----------------|------------|
| 6 | Anil | 1998-05-01 | 2010-07-26 |
| 12 | Viktor | 1998-05-01 | (null) |
| 14 | Sherry | 1998-05-01 | 2008-09-19 |
| 15 | Juan | 1998-03-23 | (null) |
| 48 | Susan | 1998-07-01 | 2009-01-03 |
| 50 | Kevin | 1998-07-02 | 2012-05-01 |
| 50 | Kevin | 2013-12-01 | 2015-02-15 |
declare #employees table (EE int, Name varchar(20));
insert #employees values
(6,'Anil'),
(12,'Viktor'),
(14,'Sherry'),
(15,'Juan'),
(48,'Susan'),
(50,'Kevin');
declare #EEChange table (EENo int, Action_type char, Action_date date);
insert #EEChange values
(6,'H','5/1/1998'),
(6,'T','7/26/2010'),
(12,'H','5/1/1998'),
(12,'R','4/16/2012'),
(14,'H','5/1/1998'),
(14,'R','9/13/2006'),
(14,'T','9/19/2008'),
(15,'H','3/23/1998'),
(48,'H','7/1/1998'),
(48,'R','10/21/2008'),
(48,'T','1/3/2009'),
(50,'H','7/2/1998'),
(50,'R','7/16/2010'),
(50,'T','5/1/2012'),
(50,'R','12/1/2013'),
(50,'T','2/15/2015');
;with CTE AS (
SELECT
LTRIM(employees.EE) as [EE No],employees.Name,
row_number() OVER (PARTITION BY employees.EE ORDER BY EEChange.action_date) AS [Seq],
EEChange.action_type,
CASE WHEN EEChange.action_type = 'H' OR EEChange.action_type = 'R' THEN
EEChange.action_date
END AS [Effective Date],
CASE WHEN EEChange.action_type = 'T' THEN
EEChange.action_date
END AS [Term Date]
FROM #employees employees
LEFT OUTER JOIN #EEChange EEchange ON employees.EE = EEChange.EEno
)
select [EE No],Name,MIN([Effective Date])[Effective Date],MIN([Term Date])[Term Date] from CTE
GROUP BY [EE No],Name
UNION
select [EE No],Name,MAX([Effective Date])[Effective Date],MAX([Term Date])[Term Date] from CTE
where Seq > 3
group by [EE No],Name

Transact SQL - which Join to Use

I have two simple SELECT statements:
The first shows a list of Features.
SELECT * FROM Features
id name
-- ----
1 24 Hour Access
2 24 hour CCTV monitoring
3 Airport location
4 Break-Out Areas
5 Business Lounge
6 Business park location
snip..
and the second statement shows a list of feature information that has changed
SELECT
*
FROM
#SmartFeaturesToUpdate new_features
ORDER BY
new_features.centre_translation_id,
new_features.feature_id,
new_features.feature_selected
feature_id centre_translation_id feature_selected
---------- --------------------- ----------------
1 1 1
2 1 1
5 1 1
10 1 1
11 1 1
snip..
What I want to see is all of the features by centre translation.
Combining the tables gives me:
SELECT
*
FROM
#SmartFeaturesToUpdate new_features
LEFT JOIN Feature feature ON feature.id = new_features.feature_id
ORDER BY
new_features.centre_translation_id,
new_features.feature_id,
new_features.feature_selected
feature_id centre_translation_id feature_selected id name
---------- --------------------- ---------------- -- ----
1 1 1 1 24 Hour Access
2 1 1 2 24 hour CCTV monitoring
5 1 1 5 Business Lounge
10 1 1 10 Double Glazing
11 1 1 11 Elevator
snip..
The result above is missing feature id's 3 and 4, because they are not in the second list.
but the result I need is:
feature_id centre_translation_id feature_selected id name
---------- --------------------- ---------------- -- ----
1 1 1 1 24 Hour Access
2 1 1 2 24 hour CCTV monitoring
3 1 1 3 Airport Location
4 1 1 4 Break-Out Area
5 1 1 5 Business Lounge
snip..
How should I modify the third SELECT statement to acheive this and combine the results from both the features and feature information list?
As the comments alluded, I needed another table which linked Features to centre_translation_ids
First get all of the feature / centre_translation varients
SELECT
[centre_translation_id] = centre_translation.id,
feature.id,
feature.name
INTO #AllTheFeatures
FROM
CentreTranslation centre_translation
CROSS JOIN Feature feature
ORDER BY
centre_translation.id,
feature.id
Now we can simply perform the LEFT JOIN
SELECT
all_features.centre_translation_id,
all_features.id,
all_features.name,
smart_features.feature_selected
FROM
#AllTheFeatures all_features
LEFT JOIN #SmartFeaturesToUpdate smart_features ON smart_features.centre_translation_id = all_features.centre_translation_id AND
smart_features.feature_id = all_features.id
ORDER BY
all_features.centre_translation_id,
all_features.id
This gives the results:
centre_translation_id id name feature_selected
--------------------- -- ---- ----------------
1 1 24 Hour Access 1
1 2 24 hour CCTV monitoring 1
1 3 Airport location NULL
1 4 Break-Out Areas NULL
1 5 Business Lounge 1
Why don't you just put it in one query?
SELECT
centre_translation.id AS centre_translation_id,
feature.id,
feature.name,
smart_features.feature_selected
FROM
CentreTranslation centre_translation
CROSS JOIN Feature feature
LEFT JOIN #SmartFeaturesToUpdate smart_features
ON smart_features.centre_translation_id = all_features.centre_translation_id
AND smart_features.feature_id = all_features.id
ORDER BY
centre_translation.centre_translation_id,
feature.id

Resources