Comparing rows with another rows in a single SQL Server Table - sql

I've a table with the following rows of data.
EngID Tower Billing Amt
100 ICS Y 5000
100 EDT Y 7777
100 ICS N 2000
and I want the result set to be consolidated by Tower & Eng ID and the amount put into the appropriate column (Invoiced or Not Invoiced) based on Billing criteria. So, below is how the final result set should look for the above table:
EngID Tower Inv Amt (Amt when Billing = Y) Non-Invoiced Amt (Billing=N)
100 ICS 5000 2000
100 EDT 7777
I'm able to get the 1st row of the result set by using the below query:
Select Temp1.Tower, Temp1. EngID, Temp2.InvoiceAmt as [Inv Amt], Temp1.InvoiceAmt AS [Non-Invoiced Amt] from
(
SELECT EngID, TOWER,BILLING, InvoiceAmt,RANK() OVER (PARTITION BY EngID, TOWER ORDER BY BILLING) AS RNK
FROM [GDF].[dbo].[Sample] ) Temp1 INNER JOIN (SELECT EngID, TOWER,Billing,InvoiceAmt, RANK() OVER (PARTITION BY EngID, TOWER ORDER BY BILLING) AS RNK
FROM [GDF].[dbo].[Sample] ) Temp2 ON
Temp1.EngID = Temp2.EngID
AND (Temp1.Tower = Temp2.Tower AND Temp1.Billing < Temp2.Billing)
However, struggling to get the second row result. My plan is to get the two rows through two separate queries and then do a union to combine the results.

One method is conditional aggregation:
select s.engid, s.tower,
sum(case when s.billing = 'Y' then amt end) as billing_y,
sum(case when s.billing = 'N' then amt end) as billing_n
from gdf.dbo.sample s
group by s.engid, s.tower;

Try this:
select engid, tower,
sum(case when billing = 'Y' then amt end) Inv_amt,
sum(case when billing = 'N' then amt end) Non_Inv_amt,
from my_table
group by
engid,
tower;

We can also do it using OUTER APPLY as below:
select A.EngID,
sum(A.Amt) as [Inv Amt (Amt when Billing = Y)],
sum(B.Amt) as [Non-Invoiced Amt (Billing=N)]
from #test A
outer apply(select b.Amt from #test B where A.EngID = b.EngID and b.tower = a.tower and B.Billing = 'n') B
where a.billing = 'y'
group by A.EngID, A.Tower
Simple LEFT JOIN:
select A.EngID,
sum(A.Amt) as [Inv Amt (Amt when Billing = Y)],
sum(B.Amt) as [Non-Invoiced Amt (Billing=N)]
from #test A
left join #test B on A.EngID = b.EngID
and b.tower = a.tower
and B.Billing = 'n'
where a.billing = 'y'
group by A.EngID, A.Tower

This code will give the desired result without any complexity.Please do find the snapshot of output from below mentioned query.Hope I solved your problem.
WITH Mycte
AS
(
Select ENGID,Tower,Case When Billing='Y' Then ISNULL(SUM(Amt),0) END AS Inv_Amt,
Case When Billing='N' Then ISNULL(SUM(Amt),0) END AS Non_Inv_Amt from #Sample
group by ENGID,Tower,Billing
)
Select ENGID,Tower,SUM(Inv_Amt) AS Inv_Amt,SUM(Non_Inv_Amt) AS Non_Inv_Amt from mycte
group by ENGID,Tower

Related

How to do this query using self join or anything but without using window function

Below is the solution but I want to know other ways to accomplish the same results (preferably in PostgreSQL).
This is the DB
Question - How many customers have churned straight after their initial free trial? what percentage is this rounded to the
nearest whole number?
WITH ranking AS (
SELECT
s.customer_id,
s.plan_id,
p.plan_name,
ROW_NUMBER() OVER (
PARTITION BY s.customer_id
ORDER BY s.plan_id) AS plan_rank
FROM dbo.subscriptions s
JOIN dbo.plans p
ON s.plan_id = p.plan_id)
SELECT
COUNT(*) AS churn_count,
ROUND(100 * COUNT(*) / (
SELECT COUNT(DISTINCT customer_id)
FROM dbo.subscriptions),0) AS churn_percentage
FROM ranking
WHERE plan_id = 4 -- Filter to churn plan
AND plan_rank = 2
You can achieve the same results with a single aggregation on customer_id with a few CASE WHEN statements:
SELECT count(*) as total_customers
,count(case when total_subscriptions = 2
and includes_free = 1
and includes_churn = 1 then 1 end) as churn_count
,100 * count(case when total_subscriptions = 2
and includes_free = 1
and includes_churn = 1 then 1 end) / count(*) as target_percent
FROM (
SELECT customer_id
,count(*) as total_subscriptions
,max(case when plan_id = 0 then 1 else 0 end) as includes_free
,max(case when plan_id = 4 then 1 else 0 end) as includes_churn
FROM dbo.subscriptions
GROUP BY customer_id
) AS tbl
-- Remove any records for people who didnt use the free trial
-- or people who are still on the free trial
WHERE includes_free = 1 AND total_subscriptions > 1
The difference between our solutions are:
Yours doesn't specify that the customer actually had a free trial
Mine doesn't include customers who went from Free -> Churn -> (something else)
Depending on your requirements you might want to make further alterations/use a different approach.

aliasing pivot column

i am trying to alias the pivot column to scenario1, scenario2, scenario3 instead of 1,2,3. I am getting error.
select *
from (select *
from (select s.campaign_id campaign_id, s.scenario_index scenario_index
from scenario s, campaign c where s.campaign_id = c.campaign_id)
pivot (max(scenario_index)for scenario_index in (1,2,3))
)a
thank you, aggregation gives the result with alias now. The requirement i have is to combine these columns with another query which is
select CASE WHEN AWARD_TYPE = 0 THEN award_rate||' points'
when AWARD_TYPE = 1 then Award_rate||' %'
when award_type=2 then RATIO_POINTS||' points per '||RATIO_MON_UNIT||' AED' End
from points_rule p
where c.pt_basic_rule_id = p.point_rule_id ) as pool_awards,
this query comes as a column and then the scenario1, 2,3 should come as 3 columns with the value of the pool_award based on the campaign_id
Just use conditional aggregation:
select s.campaign_id,
max(case when scenario_index = 1 then 1 end) as scenario1,
max(case when scenario_index = 2 then 1 end) as scenario2,
max(case when scenario_index = 3 then 1 end) as scenario3
from scenario s join
campaign c
on s.campaign_id = c.campaign_id
group by campaign_id;
You can use an alias in IN clause of the PIVOT as follows:
select *
from (select *
from (select s.campaign_id campaign_id, s.scenario_index scenario_index
from scenario s, campaign c where s.campaign_id = c.campaign_id)
pivot (max(scenario_index)for scenario_index in (1 as scenario1,2 as scenario2,3 as scenario3))
)a

SQL Joined Tables - Multiple rows on joined table per 'on' matched field merged into one row?

I have two tables I am pulling data from. Here is a minimal recreation of what I have:
Select
Jobs.Job_Number,
Jobs.Total_Amount,
Job_Charges.Charge_Code,
Job_Charges.Charge_Amount
From
DB.Jobs
Inner Join
DB.Job_Charges
On
Jobs.Job_Number = Job_Charges.Job_Number;
So, what happens is that I end up getting a row for each different Charge_Code and Charge_Amount per Job_Number. Everything else on the row is the same. Is it possible to have it return something more like:
Job_Number - Total_Amount - Charge_Code[1] - Charge_Amount[1] - Charge_Code[2] - Charge_Amount[2]
ETC?
This way it creates one line per job number with each associated charge and amount on the same line. I have been reading through W3 but haven't been able to tell definitively if this is possible or not. Anything helps, thank you!
To pivot your resultset over a fixed number of columns, you can use row_number() and conditional aggregation:
select
job_number,
total_amount,
max(case when rn = 1 then charge_code end) charge_code1,
max(case when rn = 1 then charge_amount end) charge_amount1,
max(case when rn = 2 then charge_code end) charge_code2,
max(case when rn = 2 then charge_amount end) charge_amount2,
max(case when rn = 3 then charge_code end) charge_code3,
max(case when rn = 3 then charge_amount end) charge_amount3
from (
select
j.job_number,
j.total_amount,
c.charge_code,
c.charge_amount,
row_number() over(partition by job_number, total_amount order by c.charge_code) rn
from DB.Jobs j
inner join DB.Job_Charges c on j.job_number = c.job_number
) t
group by job_number, total_amount
The above query handes up to 3 charge codes and amounts par job number (ordered by job codes). You can expand the select clause with more max(case ...) expressions to handle more of them.

Select the records that have some value but at the same time have not other value

Before starting, thank you for your help.
My problem is: I have a table, for example, activity_records, that has a date column and a activity_id column.
I want to select only those dates that, for example, have the activity_id value of 1 but do not have the value of 2 in any of the other records having the same date.
I have tried multi-select queries and all that.
One method uses not exists:
select ar.*
from activity_records ar
where ar.activity_id = 1 and
not exists (select 1
from activity_records ar2
where r2.date = ar.date and ar2.activity_id = 2
);
Because you only want dates, an alternative is group by:
select ar.date
from activity_records ar
group by ar.date
having sum(case when ar.activity_id = 1 then 1 else 0 end) > 0 and
sum(case when ar.activity_id = 2 then 1 else 0 end) = 0;
Or, most simply for this particular case:
select ar.date
from activity_records ar
where ar.activity_id in (1, 2)
group by ar.date
having max(ar.activity_id) = 1 ;
SELECT [date] FROM [activity_records] WHERE [activity_id] = 1
EXCEPT
SELECT [date] FROM [activity_records] WHERE [activity_id] = 2
See also WHERE NOT EXISTS and APPLY for more complicated scenarios.
Another way is to take only those dates with one distinct activity_id against it and then filtering those dates with activity_id = 1 by a self join.
SELECT DISTINCT B.DATE
FROM
(SELECT DATE, COUNT(DISTINCT ACTIVITY) AS COUNT_ACTIVITIES
FROM YOUR_TABLE
GROUP BY DATE
HAVING COUNT(DISTINCT ACTIVITY) = 1) A
INNER JOIN
YOUR_TABLE B
ON A.DATE = B.DATE
AND B.ACTIVITY_ID = 1;
One method using not in :
select ar.* from activity_records ar where ar.activity_id = 1 and ar.date not in (select ar2.date from activity_records ar2 where ar2.activity_id = 2 );

How to save in a variable the value of the following query? SQL Server

I need to implement two variables for save the data result in a query.
I have he following query:
SELECT * FROM
(SELECT location AS Location, COUNT(*) AS Trucks FROM Truck GROUP BY location) loc
OUTER APPLY
(
SELECT
COUNT(*) AS TotalOfCampaings,
SUM(CASE WHEN cc.campaing_status = 'Complete' THEN 1 ELSE 0 END) AS CampaingsWithCompleteStatus,
SUM(CASE WHEN cc.campaing_status = 'InProcess' THEN 1 ELSE 0 END) AS CampaingsWithInProcessStatus
FROM CampaingControl cc INNER JOIN Truck t ON cc.vin = t.vin
WHERE t.location = loc.location
) stat
This query shows the next table:
|Location|Trucks|TotalOfCampaings|CampaingsWithCompleteStatus|CampaingsWithInProcessStatus
I need to add a column at the end, in the new column i need to get the percent of campaings with complete status, i tried to do something like this:
Percent = (CampaingsWithCompleteStatus / TotalOfCamapings) * 100
But i dont know how to save the values of the query to do that.
Something like this:
SELECT
loc.Location,
loc.Trunks,
stat.TotalOfCampaings,
stat.CampaingsWithCompleteStatus,
stat.CampaingsWithInProcessStatus,
(1.0 * stat.CampaingsWithCompleteStatus /stat.TotalOfCampaings) * 100 as [Percent]
FROM
(SELECT location AS Location, COUNT(*) AS Trucks FROM Truck GROUP BY location) loc
OUTER APPLY
(
SELECT
COUNT(*) AS TotalOfCampaings,
SUM(CASE WHEN cc.campaing_status = 'Complete' THEN 1 ELSE 0 END) AS CampaingsWithCompleteStatus,
SUM(CASE WHEN cc.campaing_status = 'InProcess' THEN 1 ELSE 0 END) AS CampaingsWithInProcessStatus
FROM CampaingControl cc INNER JOIN Truck t ON cc.vin = t.vin
WHERE t.location = loc.location
) stat