SQL Server Query with join and merge two row into single row of record - sql

I have had a sample table like these
I would like to have a final result for my query in this way
I have no clue how to create SQL Server Query to archive the result as said about. Would you mind to guide me how to make it works?
Regards,

Assuming you have at most two rows, you can use row_number() to enumerate the values and conditional aggregation (or pivot, if you prefer):
select m.movementid, m.arrflt, m.depflt,
sum(case when seqnum = 1 then des else 0 end) as des_1,
sum(case when seqnum = 1 then cargo else 0 end) as cargo_1,
sum(case when seqnum = 1 then mail else 0 end) as mail_1,
sum(case when seqnum = 1 then luggage else 0 end) as luggage_1,
sum(case when seqnum = 2 then des else 0 end) as des_2,
sum(case when seqnum = 2 then cargo else 0 end) as cargo_2,
sum(case when seqnum = 2 then mail else 0 end) as mail_2,
sum(case when seqnum = 2 then luggage else 0 end) as luggage_2
from movement m join
(select md.*,
row_number() over (partition by movementid order by movementid) as seqnum
from movementdetail md
) md
on md.movementid = m.movementid
group by m.movementid, m.arrflt, m.depflt;

Related

SQL - New customers Retention - MoM

I am trying to identify the retention period of new customers that we acquire every month.
Have identified the New Customer logic from the transactions, I have no lead on how to start M1 to M10
I need to get something like below, to explain the table, in the month of Jan we have acquired 2500 customers, of those 2.5k new customers only 1600 transacted in M1(Feb), of those 1600 only 1200 transacted in M2(Mar) and so on
Similarly, in the month of Feb we have acquired 2k customers, of those only 1100 transacted in M1(here M1 refers to Mar), of those 1100 only 800 transacted in M2(here M2 refers to Apr)
M2 is subset of M1, M3 is subset of M2 and so on.
Using SQL Server 2012, I want to avoid preprocessing of data due to certain limitation on my role and access.
Any leads with sql logic will help.
Based on Gordon's answer, I propose the solution: http://sqlfiddle.com/#!18/f6785/3
select
year(first_yyyymm),
month(first_yyyymm),
count(distinct customer_id) as new_customers,
sum(case when seqnum = 1 then 1 else 0 end) as m1,
sum(case when seqnum = 2 then 1 else 0 end) as m2,
sum(case when seqnum = 3 then 1 else 0 end) as m3,
sum(case when seqnum = 4 then 1 else 0 end) as m4,
sum(case when seqnum = 5 then 1 else 0 end) as m5,
sum(case when seqnum = 6 then 1 else 0 end) as m6,
sum(case when seqnum = 7 then 1 else 0 end) as m7,
sum(case when seqnum = 8 then 1 else 0 end) as m8,
sum(case when seqnum = 9 then 1 else 0 end) as m9,
sum(case when seqnum = 10 then 1 else 0 end) as m10
from
(
select
customer_id,
first_yyyymm, yyyymm,
datediff(month, first_yyyymm, yyyymm) as seqnum
from
(
select
customer_id,
eomonth(created_at) as yyyymm,
min(eomonth(created_at))
over (partition by customer_id) as first_yyyymm
from transactions t
group by customer_id, eomonth(created_at)
) t
) t
group by year(first_yyyymm), month(first_yyyymm)
order by month(first_yyyymm);
For the data:
The result shall be:
Edit
Here's another solution, computing just those customers with transactions in every month.
http://sqlfiddle.com/#!18/ad3803/2
I would suggest the following:
Summarize by customer and month.
Get the earliest month a customer appears, using window functions.
Get the last month where there is no following month using row_number()
Aggregate.
In SQL, this looks like:
select year(first_yyyymm), month(first_yyyymm),
count(*) as new_customers,
sum(case when seqnum = 1 then 1 else 0 end) as m1,
sum(case when seqnum = 2 then 1 else 0 end) as m2,
sum(case when seqnum = 3 then 1 else 0 end) as m3,
sum(case when seqnum = 4 then 1 else 0 end) as m4,
sum(case when seqnum = 5 then 1 else 0 end) as m5,
sum(case when seqnum = 6 then 1 else 0 end) as m6,
sum(case when seqnum = 7 then 1 else 0 end) as m7,
sum(case when seqnum = 8 then 1 else 0 end) as m8,
sum(case when seqnum = 9 then 1 else 0 end) as m9,
sum(case when seqnum = 10 then 1 else 0 end) as m10
from (select customer, eomonth(date) as yyyymm,
min(eomonth(date)) over (partition by customer) as first_eomonth,
row_number() over (partition by customer order by eomonth(date)) as seqnum
from transactions t
group by customer, eomonth(date)
) t
where datediff(month, first_yyyymm, yyyymm) = seqnum - 1
group by year(first_yyyymm), month(first_yyyymm)
order by min(first_yyyymm);

Combining two aggregate queries into one

For some context, I am making an image browser which is connected to an SQLite database. Within the browser, similar images are grouped into an event (EventId) and each image (MicrosoftId) is labelled with a few tags (name).
I have these two queries on the same table (TagsMSCV) but pulling out different information. Ultimately I need to combine the information in my browser so if it was possible to combine these two queries (maybe with a JOIN?) it would be a lot faster and convenient for me. Both results of these queries share the EventId column.
1st Query ():
SELECT EventId as 'event', count(*) as 'size',
SUM(case when tag_count = 1 then 1 else 0 end) as '1',
SUM(case when tag_count = 2 then 1 else 0 end) as '2',
SUM(case when tag_count = 3 then 1 else 0 end) as '3'
FROM (SELECT EventId, MicrosoftId,
SUM(case when name in ('indoor', 'cluttered', 'screen') then 1 else 0 end) as tag_count
FROM TagsMSCV GROUP BY EventId, MicrosoftId) TagsMSCV
GROUP BY EventId ORDER BY 3 DESC, 2 DESC, 1 DESC
2nd Query
SELECT EventId,
SUM(CASE WHEN name = 'indoor' THEN 1 ELSE 0 END) as indoor,
SUM(CASE WHEN name = 'cluttered' THEN 1 ELSE 0 END) as cluttered,
SUM(CASE WHEN name = 'screen' THEN 1 ELSE 0 END) as screen
FROM TagsMSCV WHERE name IN ('indoor', 'cluttered', 'screen')
GROUP BY EventId
As you can see in both queries I am feeding in the tags 'necktie' 'man', 'male' and getting different information back.
SQL Fiddle Here: https://www.db-fiddle.com/f/f8WNimjmZAj1XXeCj4PHB8/3
You should do this all in one query:
SELECT EventId as event, count(*) as size,
SUM(case when (indoor + cluttered + screen) = 1 then 1 else 0 end) as tc_1,
SUM(case when (indoor + cluttered + screen) = 2 then 1 else 0 end) as tc_2,
SUM(case when (indoor + cluttered + screen) = 3 then 1 else 0 end) as tc_3,
SUM(indoor) as indoor,
SUM(cluttered) as cluttered,
SUM(screen) as screen
FROM (SELECT EventId, MicrosoftId,
SUM(CASE WHEN name = 'indoor' THEN 1 ELSE 0 END) as indoor,
SUM(CASE WHEN name = 'cluttered' THEN 1 ELSE 0 END) as cluttered,
SUM(CASE WHEN name = 'screen' THEN 1 ELSE 0 END) as screen
FROM TagsMSCV
GROUP BY EventId, MicrosoftId
) TagsMSCV
GROUP BY EventId
ORDER BY 3 DESC, 2 DESC, 1 DESC;
You need two aggregations to get the information about the tag counts. There is no need to add more aggregations and joins to the query.
You could use an Inner join subquery
SELECT TagsMSCV.EventId as 'event', count(*) as 'size',
SUM(case when tag_count = 1 then 1 else 0 end) as '1',
SUM(case when tag_count = 2 then 1 else 0 end) as '2',
SUM(case when tag_count = 3 then 1 else 0 end) as '3',
t.necktie,
t.man,
t.male
FROM (
SELECT EventId, MicrosoftId,
SUM(case when name in ('necktie' 'man', 'male') then 1 else 0 end) as tag_count
FROM TagsMSCV GROUP BY EventId, MicrosoftId
) TagsMSCV
INNER JOIN (
SELECT EventId,
SUM(CASE WHEN name = 'necktie' THEN 1 ELSE 0 END) as necktie,
SUM(CASE WHEN name = 'man' THEN 1 ELSE 0 END) as man,
SUM(CASE WHEN name = 'male' THEN 1 ELSE 0 END) as male
FROM TagsMSCV WHERE name IN ('necktie' 'man', 'male')
GROUP BY EventId
) t on t.EventId = TagsMSCV.EventId
GROUP BY TagsMSCV.EventId
ORDER BY 3 DESC, 2 DESC, 1 DESC

How to make multiple rows into columns

I've tried MAX CASE WHEN and CTE but for some reason can't exactly figure this out.
My data looks like this:
SELECT RC, isMHy, eligible
FROM test
RC isMHY eligible
190B05 0 1
190K00 1 0
There can be up to 4 rows in the table, I want to the results to look like this (12 columns in case there are 4 rows)
RC1 isMHY1 eligible1 RC2 isMHY2 eligible2
190B05 0 1 190K00 1 0
Any suggestions would be appreciated
You can use conditional aggregation with ROW_NUMBER() :
SELECT MAX(CASE WHEN s.rnk = 1 THEN s.rc END) as rc1,
MAX(CASE WHEN s.rnk = 1 THEN s.ismhy END) as ismhy1,
MAX(CASE WHEN s.rnk = 1 THEN s.eligible END) as eligible1,
MAX(CASE WHEN s.rnk = 2 THEN s.rc END) as rc2,
MAX(CASE WHEN s.rnk = 2 THEN s.ismhy END) as ismhy2,
MAX(CASE WHEN s.rnk = 2 THEN s.eligible END) as eligible2,
..........
FROM(
SELECT t.*,
ROW_NUMBER() OVER(ORDER BY SELECT 1) as rnk
FROM test t) s

JOIN always the default value, else join the match value

I have the following SQL Server Query
select r.isactive,r.workingyear,r.startperiod,r.endperiod,r.anniversary
from setup_holiday_policy t cross apply
(select data
from dbo.Split(t.scheduleapplication, ',')
) di cross apply
(select max(case when did.id = 1 then did.data end) as isactive,
max(case when did.id = 2 then did.data end) as workingyear,
max(case when did.id = 3 then did.data end) as anniversary,
max(case when did.id = 4 then did.data end) as startperiod,
max(case when did.id = 5 then did.data end) as endperiod
from dbo.Split(di.data,':') did
) r
WHERE r.workingyear = #employeeworkingyears
The policy table can have a 0 value in the workingyear field. Meaning that when this field has 0 then is the default record I should return.
setup_holiday_policy
So, if #employeeworkingyears = 2 and there is no workingyears = 2 in setup_holiday_policy I should return the default row that has the 0 value in workingyears field.
This is a sample of the rows returned.
Any clue how to achieve this?
If only one row is going to be returned (as suggested by the sample data), you can do this using top:
select top 1 r.isactive,r.workingyear,r.startperiod,r.endperiod,r.anniversary
from setup_holiday_policy t cross apply
(select data
from dbo.Split(t.scheduleapplication, ',')
) di cross apply
(select max(case when did.id = 1 then did.data end) as isactive,
max(case when did.id = 2 then did.data end) as workingyear,
max(case when did.id = 3 then did.data end) as anniversary,
max(case when did.id = 4 then did.data end) as startperiod,
max(case when did.id = 5 then did.data end) as endperiod
from dbo.Split(di.data,':') did
) r left outer join
(select #employeeworkingyears as employeeworkingyears
) e
on
WHERE r.workingyear in (#employeeworkingyears, 0)
order by r.workingyear desc;

How to do addition and division of aliased columns in a query?

I am using SQL Server 2008.
I am trying to do some basic math in some basic queries. I need to add up wins, losses, total, and percentages. I usually ask for the raw numbers and then do the calculations once I return my query to page. I would like to give SQL Server the opportunity to work a little harder.
What I want to do is something like this:
SELECT SUM(case when vote = 1 then 1 else 0 end) as TotalWins,
SUM(case when vote = 0 then 1 else 0 end) as TotalLosses,
TotalWins + TotalLosses as TotalPlays,
TotalPlays / TotalWins as PctWins
Here's what I am doing now:
SELECT SUM(case when vote = 1 then 1 else 0 end) as TotalWins,
SUM(case when vote = 0 then 1 else 0 end) as TotalLosses,
SUM(case when vote = 1 then 1 else 0 end) + SUM(case when vote = 0 then 1 else 0 end) as Votes
What is the easiest, cleanest way to do simple math calculations like this in a query?
*EDIT: *
While I got some great answers, I didn't get what I was looking for.
The scores that I will be calculating are for a specific team, so, my results need to be like this:
TeamID Team Wins Losses Totals
1 A's 5 3 8
2 Bee's 7 9 16
3 Seas 1 3 4
SELECT T.TeamID,
T.Team,
V.TotalWins,
V.TotalLosses,
V.PctWins
FROM Teams T
JOIN
SELECT V.TeamID,
SUM(case when vote = 1 then 1 else 0 end) as V.TotWin,
SUM(case when vote = 0 then 1 else 0 end) as V.TotLoss
FROM Votes V
GROUP BY V.TeamID
I tried a bunch of things, but don't quite know what wrong. I am sure the JOIN part is where the problem is though. How do I bring these two resultsets together?
One way is to wrap your query in an external one:
SELECT TotalWins,
TotalLosses,
TotalWins + TotalLosses as TotalPlays,
TotalPlays / TotalWins as PctWins
FROM
( SELECT SUM(case when vote = 1 then 1 else 0 end) as TotalWins,
SUM(case when vote = 0 then 1 else 0 end) as TotalLosses
FROM ...
)
Another way (suggested by #Mike Christensen) is to use Common Table Expressions (CTE):
; WITH Calculation AS
( SELECT SUM(case when vote = 1 then 1 else 0 end) as TotalWins,
SUM(case when vote = 0 then 1 else 0 end) as TotalLosses
FROM ...
)
SELECT TotalWins,
TotalLosses,
TotalWins + TotalLosses as TotalPlays,
TotalPlays / TotalWins as PctWins
FROM
Calculation
Sidenote: No idea if this would mean any preformance difference in SQL-Server but you can also write these sums:
SUM(case when vote = 1 then 1 else 0 end)
as counts:
COUNT(case when vote = 1 then 1 end) --- the ELSE NULL is implied
try
select a, b, a+b as total
from (
select
case ... end as a,
case ... end as b
from realtable
) t
To answer your second question, this is the code you put forward with corrections to the syntax:
SELECT
T.TeamID,
T.Team,
V.TotalWins,
V.TotalLosses,
PctWins = V.TotalWins * 100 / CAST(V.TotalWins + V.TotalLosses AS float)
FROM Teams T
JOIN (
SELECT
TeamID,
SUM(case when vote = 1 then 1 else 0 end) as TotalWins,
SUM(case when vote = 0 then 1 else 0 end) as TotalLosses
FROM Votes
GROUP BY TeamID
) as V on T.TeamID = V.TeamID
Note the brackets around the inner select.
It might help you if you're doing this sort of thing more than once to create a view...
CREATE VIEW [Totals]
SELECT
SUM(case when T.vote = 1 then 1 else 0 end) as TotalWins,
SUM(case when T.vote = 0 then 1 else 0 end) as TotalLosses,
T.SomeGroupColumn
FROM SomeTable T
GROUP BY T.SomeGroupColumn