Fetch data in a single Row - sql

I have two tables PackageDetail and PackageDuration
PackageDuration have the PackageID as Foreign Key i.e. can have multiple records with respect to PackageID
The Schema of the PackageDetail is:
PackageID INT PK
PackageName Nvarchar(50)
Schema of the PackageDuration Table is:
DurationID INT PK
Price Money
Duration Nvarchar(50)
PackageID INT FPK
PackageDetail tables have follwoing records:
PackageID PackageName
1 TestPackage
2 MySecondPackage
PackageDuration table have following records:
DurationID PackageID Price Duration
1 1 100 6
2 1 200 12
3 1 300 24
4 2 500 6
PackageDuration table can have max 3 records with one PackageID not more than this(if have ignore that)
Now I want to select the Records as in following Way:
PackageId PackageNAme Price1 Price2 Price3 Duration1 Duration2 Duration3
1 TestPackage 100 200 300 6 12 24
2 MySecondPackage 500 null null 6 null null
Please suggest me how can I achive this.

Another approach:
WITH Durations AS
(
SELECT *, ROW_NUMBER() OVER(PARTITION BY PackageId ORDER BY DurationId) Sequence
FROM PackageDuration
)
SELECT A.PackageId, B.PackageName,
MIN(CASE WHEN Sequence = 1 THEN Price ELSE NULL END) Price1,
MIN(CASE WHEN Sequence = 2 THEN Price ELSE NULL END) Price2,
MIN(CASE WHEN Sequence = 3 THEN Price ELSE NULL END) Price3,
MIN(CASE WHEN Sequence = 1 THEN Duration ELSE NULL END) Duration1,
MIN(CASE WHEN Sequence = 2 THEN Duration ELSE NULL END) Duration2,
MIN(CASE WHEN Sequence = 3 THEN Duration ELSE NULL END) Duration3
FROM Durations A
INNER JOIN PackageDetail B
ON A.PackageId = B.PackageId
GROUP BY A.PackageId, B.PackageName

This should work as long as the durations are unique for a package and they are either 6, 12, or 24.
SELECT
PackageDetail.PackageId, PackageDetail.PackageName,
D1.Price as Price1, D2.Price as Price2, D3.Price as Price3,
D1.Duration as Duration1, D2.Duration as Duration2, D3.Duration as Duration3
FROM PackageDetail
LEFT OUTER JOIN PackageDuration D1
ON D1.PackageId = PackageDetail.PackageId AND D1.Duration = 6
LEFT OUTER JOIN PackageDuration D2
ON D2.PackageId = PackageDetail.PackageId AND D2.Duration = 12
LEFT OUTER JOIN PackageDuration D3
ON D3.PackageId = PackageDetail.PackageId AND D3.Duration = 24

;WITH pvt AS
(
SELECT PackageID,
Price1 = MAX(CASE WHEN Duration = 6 THEN Price END),
Price2 = MAX(CASE WHEN Duration = 12 THEN Price END),
Price3 = MAX(CASE WHEN Duration = 24 THEN Price END),
Duration1 = MAX(CASE WHEN Duration = 6 THEN 6 END),
Duration2 = MAX(CASE WHEN Duration = 12 THEN 12 END),
Duration3 = MAX(CASE WHEN Duration = 24 THEN 24 END)
FROM dbo.PackageDuration
GROUP BY PackageID
)
SELECT
pvt.PackageID,
p.PackageName,
pvt.Price1, pvt.Price2, pvt.Price3,
pvt.Duration1, pvt.Duration2, pvt.Duration3
FROM
dbo.PackageDetail AS p
INNER JOIN
pvt ON p.PackageID = pvt.PackageID
ORDER BY p.PackageID;

Maybe I'm missing something in the requirements, but it seems like Sql Server's PIVOT is what you're looking for.
There are quite a few questions here at SO about PIVOT... Here's a good clean example with references to other questions: How do i transform rows into columns in sql server 2005
The big benefit of a pivot table over the other answers here is that it will scale out with no modifications if you add records to your PackageDuration table in the future.

Related

TSQL Pivot table rows to columns

I've got some data in a table that looks like the following. I'm trying to run a query that will get my data on a single row per requestId. I don't need the dates or the denial reason just the eprojman and apvStatus for each groupId
requestId - projMan1 - apvStatus1 - projMan2 - apvStatus2 - projMan3 - apvStatus3 etc.. for all 5 groupIds
requestId
groupId
entryDate
approvalDate
apvStatus
projMan
denialReason
1
1
2020-11-02
2019-07-25
APPROVED
rx1942
NULL
1
2
2020-11-02
2019-07-25
APPROVED
ma2674
NULL
1
3
2020-11-02
2019-07-25
APPROVED
cb9097
NULL
1
4
2020-11-02
2019-07-25
APPROVED
bj1763
NULL
1
5
2020-11-02
2019-07-25
APPROVED
tr5972
NULL
2
1
2020-11-02
NULL
NOT APPROVED
NULL
6
2
2
2020-11-02
NULL
PENDING
ma2674
NULL
2
3
2020-11-02
NULL
PENDING
cb9097
NULL
2
4
2020-11-02
NULL
PENDING
bj1763
NULL
2
5
2020-11-02
NULL
PENDING
tr5972
NULL
I've been trying to use a PIVOT table but all the examples I find involves summing data or something. I just pretty much want to take the 5 rows and turn it into 1 for each requestID
The only thing I've been able to come up with is to select from the same table 5 times for each groupID and union it but that's slower than heck. Got to be a better way
Thanks.
Current query:
select group1.requestId
, group1.apvStatus as apvStatus1
, group1.projMan as projMan1
, group2.apvStatus as apvStatus2
, group2.projMan as projMan2
, group3.apvStatus as apvStatus3
, group3.projMan as projMan3
,group4.apvStatus as apvStatus4
, group4.projMan as projMan4
,group5.apvStatus as apvStatus5
, group5.projMan as projMan5
,group1.denialReason
INTO #TEMPBAOrganized
from (
select requestId, apvStatus, projMan, denialReason from #TEMPBULKAPPROVAL where groupId = 1) group1
INNER JOIN
(select requestId, apvStatus, projMan, denialReason from #TEMPBULKAPPROVAL where groupId = 2) group2
on group1.requestId = group2.requestId
INNER JOIN
(select requestId, apvStatus, projMan from #TEMPBULKAPPROVAL where groupId = 3) group3
on group1.requestId = group3.requestId
INNER JOIN
(select requestId, apvStatus, projMan from #TEMPBULKAPPROVAL where groupId = 4) group4
on group1.requestId = group4.requestId
INNER JOIN
(select requestId, apvStatus, projMan from #TEMPBULKAPPROVAL where groupId = 5) group5
on group1.requestId = group5.requestId
For pivoting of multiple column, it is easier to use CASE expression with aggregate.
select t.requestId,
projMan1 = max(case when t.groupId = 1 then t.projMan end),
apvStatus1 = max(case when t.groupId = 1 then t.apvStatus end),
projMan2 = max(case when t.groupId = 2 then t.projMan end),
apvStatus2 = max(case when t.groupId = 2 then t.apvStatus end),
projMan3 = max(case when t.groupId = 3 then t.projMan end),
apvStatus3 = max(case when t.groupId = 3 then t.apvStatus end),
projMan4 = max(case when t.groupId = 4 then t.projMan end),
apvStatus4 = max(case when t.groupId = 4 then t.apvStatus end),
projMan5 = max(case when t.groupId = 5 then t.projMan end),
apvStatus5 = max(case when t.groupId = 5 then t.apvStatus end)
from #TEMPBULKAPPROVAL t
group by t.requestId
Note : max is also an aggregate function
You can do it with something like this:
SELECT
requestId,
approvalDate_1 = MAX(approvalDate_1),
approvalDate_2 = MAX(approvalDate_2),
approvalDate_3 = MAX(approvalDate_3),
approvalDate_4 = MAX(approvalDate_4),
approvalDate_5 = MAX(approvalDate_5),
projMan_1 = MAX(projMan_1),
projMan_2 = MAX(projMan_2),
projMan_3 = MAX(projMan_3),
projMan_4 = MAX(projMan_4),
projMan_5 = MAX(projMan_5)
FROM
(
SELECT
requestId,
groupId,
approvalDate,
projMan,
'approvalDate_' + CAST( groupId AS VARCHAR(2)) AS approvalDatePivot,
'projMan_' + CAST( groupId AS VARCHAR(2)) AS projManPivot
FROM
#tbl
) T
PIVOT (
MAX(approvalDate) FOR approvalDatePivot IN ([approvalDate_1],[approvalDate_2],[approvalDate_3],[approvalDate_4],[approvalDate_5])
) pvt_1
PIVOT (
MAX(projMan) FOR projManPivot IN ([projMan_1],[projMan_2],[projMan_3],[projMan_4],[projMan_5])
) pvt_2
GROUP BY requestId

Insert records from multiple rows of table to multiple columns of other table

I have an existing table structure with sample data like
Id
BookingId
Value
TypeId
AddedTime
1
100
10
T1
2021-03-22 08:51:52.6333333
2
100
20
T2
2021-03-22 08:50:55.8133333
3
100
30
T3
2021-03-22 08:50:22.1033333
4
200
50
T1
2021-03-22 08:50:22.1033333
5
200
60
T2
2021-03-22 08:50:22.1000000
6
200
70
T3
2021-03-22 08:50:22.0800000
and now data model is changed and it becomes like
Id
BookingId
Type1Value
Type2Value
Type3Value
AddedTime
Please help me what would be query to copy data from previous table to new table.
Output should be something like
Id
BookingId
Type1Value
Type2Value
Type3Value
AddedTime
1
100
10
20
30
2
200
50
60
70
I tried:
select BookingId
, Type1Value = max(case when RN=1 then Value else null end)
, Type2Value = max(case when RN=2 then Value else null end)
, Type3Value = max(case when RN=3 then Value else null end)
from (
select *
, rn = Row_Number() over (Partition By TypeId Order by AddedTime)
from Values_M
) a
where rn <= 3
group by BookingId
This will gives you the required result using conditional case expression.
Using row_number() to generate new running number Id
select Id = row_number() over (order by BookingId),
BookingId = BookingId,
Type1Value = max(case when TypeId = 'T1' then Value end),
Type2Value = max(case when TypeId = 'T2' then Value end),
Type3Value = max(case when TypeId = 'T3' then Value end),
AddedTime = min(AddedTime)
from Values_M
group by BookingId
dbfiddle
select BookingId, min(T1) as Type1Value, min(T2) as Type2Value, min(T3) as Type3Value
from table1
pivot (sum(value) for Typeid in (T1,T2,T3)) as PivotTable
group by BookingId
You can use row_number() and self join.
with cte as (
select Id, BookingId, [Value], TypeId, AddedTime
, row_number() over (partition by BookingId order by id asc) rn
from Values_M
)
select C1.rn, C1.BookingId, C1.[Value] Type1Value, C2.[Value] Type2Value, C3.[Value] Type3Value, C1.AddedTime
from cte C1
inner join cte C2 on C2.BookingId = C1.BookingId and C2.rn = 2
inner join cte C3 on C3.BookingId = C1.BookingId and C3.rn = 3
where C1.rn = 1
order by BookingId asc;

To select data from multiple records in SQL Server having a common ID

I need to select/concat data from 2 tables in SQL Server I'm using Left Join, but the data is returned as multiple records.
Below are the sample tables
Table1
Id Name Age
1 Sk 20
2 Rb 30
Table2
ID Bike Price Table1Id
1 RX 200 1
2 CD 250 1
3 FZ 300 1
4 R1 400 2
The desired output is
ID Name Age Bike1 Price1 Bike2 Price2 Bike3 Price3
1 Sk 20 RX 200 CD 250 FZ 300
2 Rb 30 R1 400 NULL NULL NULL NULL
A sample format of the query I'm using
SELECT A.ID, A.Name, B.Bike, B.Price FROM Table1 A LEFT JOIN Table2 B ON
A.id = B.Table1Id order by A.id
The output I'm getting from the above query is
ID Name Age Bike Price
1 Sk 20 RX 200
1 Sk 20 CD 250
1 Sk 20 FZ 300
2 Rb 30 R1 400
I need the data as one record for a particular ID and not multiple records (As seen in the desired output). Tired using offset, but offset will return only limited result not the entire records.
Any suggestions on how this can be achieved?
If you know the maximum number of bikes per person, you can use conditional aggregation:
SELECT ID, Name,
MAX(CASE WHEN seqnm = 1 THEN Bike END) as bike_1,
MAX(CASE WHEN seqnm = 1 THEN Price END) as price_1,
MAX(CASE WHEN seqnm = 2 THEN Bike END) as bike_2,
MAX(CASE WHEN seqnm = 2 THEN Price END) as price_2,
MAX(CASE WHEN seqnm = 3 THEN Bike END) as bike_3,
MAX(CASE WHEN seqnm = 3 THEN Price END) as price_3
FROM (SELECT A.ID, A.Name, B.Bike, B.Price,
ROW_NUMBER() OVER (PARTITION BY A.id ORDER BY B.Price) as seqnum
FROM Table1 A LEFT JOIN
Table2 B
ON A.id = B.Table1Id
) ab
GROUP BY ID, Name,
ORDER BY id

SQL: Multiple select that differ by only one condition

I've got a beginner question. My SQL table looks like:
| Date | Type | Manufacturer |
2016/04/01 A X
2016/04/01 B Y
2016/04/02 B X
2016/05/07 A Z
... ... ...
My aim is to count the quantity of "Types" by manufacturers between two dates. I would like to get a result like following:
| Manufacturer | Quantity_TypeA | Quantity_TypeB |
X 1 1
Y 0 1
Z 1 0
My query looks like:
select Manufacturer as Manufacturer,
COUNT(*) as Quantity_TypeA
From MyTable
Where [Type] = 'A' and
Date between '20150101' and '20160930',
COUNT(*) as Quantity_TypeB
From MyTable
Where [Type] = 'B' and
Date between '20150101' and '20160930'
group by Manufacturer Order by Quantity_TypeA DESC
I have also tried to use functions like CASE on the Type and it didn't work. I am missing something but what?
Try this
select Manufacturer as Manufacturer,
SUM(case when [Type] = 'A' then 1 else 0 end) as Quantity_TypeA,
SUM(case when [Type] = 'B' then 1 else 0 end) as Quantity_TypeB
From MyTable
Where
Date between '20150101' and '20160930'
group by Manufacturer
Use case expressions to do conditional counting:
select Manufacturer as Manufacturer,
COUNT(case when [Type] = 'A' then 1 end) as Quantity_TypeA,
COUNT(case when [Type] = 'B' then 1 end) as Quantity_TypeB
from MyTable
where Date between '20150101' and '20160930',
group by Manufacturer
order by Quantity_TypeA DESC
count() does only count non-null values. The case expressions either return 1 or null, i.e. only A's or B's are counted.

T-SQL multiple datetime diff on one line

Using SQL Server (T-SQL), and I have two tables:
tblTrial:
TrialID (PK) int
TrialDate
...
tblLaps:
LapID (PK) int
TrialID (FK) int
LapNumber int
LapStart smalldatetime
...
For TrialID = 1, there are four Lap rows:
LapID TrialID LapNumber LapStart
1 1 1 t1 (some smalldatetime value)
2 1 2 t2
3 1 3 t3
4 1 4 t4
I want to display the SQL so that for each Trial, only one row is displayed, and it has the time differences.
For example, a row for TrialID = 1 might look like:
Trial# 1stLap 2ndLap 3rdLap
---------------------------------
1 3min 4min 5min
where 1stLap is time difference t2-t1, 2ndLap is t3-t2, 3rdLap is t4-t3.
How do I make everything go on one line in a SQL statement?
Thanks
select
t.TrialID
, datediff(minute, max(case when LapNumber = 1 then LapStart end) , max(case when LapNumber = 2 then LapStart end) ) lap1_2
, datediff(minute, max(case when LapNumber = 2 then LapStart end) , max(case when LapNumber = 3 then LapStart end) ) lap2_3
, datediff(minute, max(case when LapNumber = 3 then LapStart end) , max(case when LapNumber = 4 then LapStart end) ) lap3_4
from tblTrial as t
inner join tblLaps as l on t.TrialID = l.TrialID
group by
t.TrialID
see this sqlfiddle demo
For time difference in minutes, you could do like this:
with laps as -- First CTE table to join every lap with the next lap to get end time
(
select TrialID,t1.LapID, datediff(mi,t1.LapStart, t2.LapStart ) as Lap
from tblLaps t1
join tblLaps t2
on t1.LapID = t2.LapID - 1 and t1.TrialID = t2.TrialID
)
select TrialID,
max(case t1.LapID when 1 then Lap else null end) as [1stLap],
max(case t1.LapID when 2 then Lap else null end) as [2ndLap],
max(case t1.LapID when 3 then Lap else null end) as [3rdLap],
from laps
group by TrialID
If you want differ by seconds, use datediff(ss,startdate , enddate ), here's the datediff document.