How to sum in sql with a DISTINCT clause - sql

In the following example SQL Fiddle
How should I proceed to obtain the cumulative price for each 'Phone' instead of obtaining the last value?
In the example given below, I would need the following table to be produced:
Phone Price Purchases
50 35 3
51 50 2
52 99 3
55 21 2
53 16 2
54 21 1
56 16 1
58 22 1
57 10 2
This is to be done in SQL-Server 2012
Thanks in advance.

You should be able to use the following:
select c1.phone,
c2.TotalPrice,
c1.purchases
from supportContacts c1
inner join
(
select
max(Fecha) maxFecha,
sum(price) TotalPrice,
phone
from supportContacts
group by phone
) c2
on c1.phone = c2.phone
and c1.Fecha = c2.maxFecha
order by c1.phone;
See SQL Fiddle with Demo.
The subquery gets the Total sum for each phone along with the the max fecha associated with the phone. You then use this and join back to your table on both the phone and the fecha to get the result.

I don't have a SQL Server 2012 handy, but give this a shot:
select
phone,
purchases,
price,
sum(price) over (partition by phone order by phone, price) as running_sum_purchases
FROM
supportContacts

Isn't it just...
SELECT Phone, Sum(Price), Count(Purchases)
FROM supportContacts
GROUP BY Phone
ORDER BY 1
.. or have I missed something?
http://sqlfiddle.com/#!6/7b36f/41
50 35 3
51 50 4
52 99 3
53 16 2
54 21 2
55 21 1
56 16 1
57 10 1
58 22 2
If you need more details per phone, you can add a subquery :
SELECT
Phone,
Sum(Price) as Total,
Count(Purchases) as Purchase_Count,
(SELECT TOP 1 Price
FROM supportContacts sc2
WHERE sc2.phone=sc1.phone
ORDER BY fecha DESC
) as Most_Recent
FROM supportContacts sc1
GROUP BY Phone
ORDER BY Phone
or, for the actual requirement which I've finally worked out :)
SELECT
Phone,
Sum(Price) as Total,
Count(Purchases) as Purchase_Count,
(SELECT Purchases
FROM supportContacts sc2
WHERE sc2.phone=sc1.phone
AND sc2.Fecha=
(SELECT Max(Fecha)
FROM supportContacts sc3
WHERE sc3.phone=sc1.phone
)
) as Last_Purchase
FROM supportContacts sc1
GROUP BY Phone
ORDER BY Phone
.. which is starting to get quite unwieldy, there's probably an optimisation possible, but I'm losing the will to play... LOL
But thanks for the cerebral exercise of trying to do it this way :)
EDIT
I would probably have done it like this, if it had been me...
http://sqlfiddle.com/#!6/7b36f/98
With PhoneGroup as
( SELECT
Phone,
Sum(Price) as Total_Price,
Max(Fecha) as Last_Fecha
FROM supportContacts
GROUP BY Phone
)
SELECT
Phone, Total_Price,
(SELECT Purchases
FROM supportContacts sc2
WHERE sc2.phone=PhoneGroup.phone
AND sc2.Fecha=PhoneGroup.Last_Fecha
) as Last_Purchase
FROM PhoneGroup
ORDER BY Phone

Related

SQL Querying of Data by grouping with only one main variable(Store) and finding the percentage of customers in other variable

Tables - Store
Stores
Date
Customer_ID
A
01/01/2020
1111
C
01/01/2020
1111
F
02/01/2020
1234
A
02/01/2020
1111
A
02/01/2020
2222
Tables - Customer
Customer_ID
Age_Group
Income_Level
1111
26-30
Low
1234
25 and below
Mid
2222
31-60
High
I want to know how I can get this output.
Stores
Age_Group
Percentage_by_Age
Income_Level
Percentage_By_Income
A
25 and below
10
Low
80
A
25 and below
10
Mid
10
A
25 and below
10
High
10
A
26 - 30
42
Low
15
A
26 - 30
42
Mid
65
A
26 - 30
42
High
20
A
31 - 60
48
Low
30
A
31 - 60
48
Mid
50
A
31 - 60
48
High
20
I am using SQL to query from different tables.
First I need to aggregate the number of customers by stores, then in each store, I want to find out how many customers visited Store A in a particular age group(25 and below), and how many of them are in which income level.
May I know how I can go about solving this query?
Thanks.
My current solution/thought process
SELECT
stores AS Stores,
Age_Group AS Age,
Income_Level AS Income
COUNT(DISTINCT(Customer_ID)) AS Number_of_Customers
FROM tables JOIN tables....
GROUP BY Stores, Ages, Income;
And then manually calculating the percentages.
But it doesn't seem right.
Is there a way to produce an example output table using just SQL?
As per your requirement, Common Table Expressions can be used . You can use below code to get the expected output.
WITH
data_for_percent_by_income AS (
SELECT
COUNT(customer_id) AS cus_count_in_per_income_level_and_agegrp,
Age_group AS age_g,income_level AS inc_lvl
FROM
`project.dataset.Customer2`
WHERE
customer_id IN (
SELECT customer_id
FROM
`project.dataset.Store5`
WHERE stores='A')
GROUP BY
Age_group,income_level),tot_cus_in_defined_income_level AS (
SELECT
COUNT(customer_id) AS cus_count_in_per_income_level,Age_group AS ag
FROM
`project.dataset.Customer2`
WHERE
customer_id IN (
SELECT
customer_id
FROM
`project.dataset.Store5`
WHERE stores='A')
GROUP BY
Age_group),
tot_cus_storeA AS(
SELECT
COUNT(*) AS tot_cus_in_A
FROM
`project.dataset.Customer2`
WHERE customer_id IN (
SELECT customer_id
FROM
`project.dataset.Store5`
WHERE stores='A') ),
final_view AS(
SELECT
ROUND(cus_count_in_per_income_level_and_agegrp*100/cus_count_in_per_income_level) AS p_by_inc,
age_g,inc_lvl
FROM
data_for_percent_by_income
INNER JOIN
tot_cus_in_defined_income_level
ON
data_for_percent_by_income.age_g=tot_cus_in_defined_income_level.ag )
SELECT
stores,tot_cus_in_defined_income_level.ag AS age_group,income_level,
ROUND(cus_count_in_per_income_level*100/tot_cus_in_A) AS percentage_by_age,
p_by_inc AS percentage_by_income
FROM
tot_cus_in_defined_income_level,tot_cus_storeA,`project.dataset.Customer2`,`project.dataset.Store5`
INNER JOIN
final_view
ON
age_group=final_view.age_g AND income_level=final_view.inc_lvl
WHERE
tot_cus_in_defined_income_level.ag = Age_group AND stores='A'
GROUP BY
stores,percentage_by_age,age_group,income_level,percentage_by_income
ORDER BY Age_group
I have attached the screenshots of the input table and output table.
Customer Table
Store Table
Output Table
SELECT
s.Stores AS Stores,
c.age_group AS Age,
a.income_level AS Affluence,
CAST(COUNT(DISTINCT c.Customer_ID) AS numeric)*100/SUM(CAST(COUNT(DISTINCT c.Customer_ID) AS numeric)) OVER(PARTITION BY s.Stores ) AS Perc_of_Members
This is what I did in the end.

Getting latest price of different products from control table

I have a control table, where Prices with Item number are tracked date wise.
id ItemNo Price Date
---------------------------
1 a001 100 1/1/2003
2 a001 105 1/2/2003
3 a001 110 1/3/2003
4 b100 50 1/1/2003
5 b100 55 1/2/2003
6 b100 60 1/3/2003
7 c501 35 1/1/2003
8 c501 38 1/2/2003
9 c501 42 1/3/2003
10 a001 95 1/1/2004
This is the query I am running.
SELECT pr.*
FROM prices pr
INNER JOIN
(
SELECT ItemNo, max(date) max_date
FROM prices
GROUP BY ItemNo
) p ON pr.ItemNo = p.ItemNo AND
pr.date = p.max_date
order by ItemNo ASC
I am getting below values
id ItemNo Price Date
------------------------------
10 a001 95 2004-01-01
6 b100 60 2003-01-03
9 c501 42 2003-01-03
Question is, is my query right or wrong? though I am getting my desired result.
Your query does what you want, and is a valid approach to solve your problem.
An alternative option would be to use a correlated subquery for filtering:
select p.*
from prices p
where p.date = (select max(p1.date) from prices where p1.itemno = p.itemno)
The upside of this query is that it can take advantage of an index on (itemno, date).
You can also use window functions:
select *
from (
select p.*, rank() over(partition by itemno order by date desc) rn
from prices p
) p
where rn = 1
I would recommend benchmarking the three options against your real data to assess which one performs better.

Count double occurrences in order list

I have a list of orders, I need to find which ones occur with code 47 more than once with different users. For example:
ORDER_ID CODE USER
111 47 1
111 47 2
222 47 1
333 47 1
333 47 2
444 47 1
The expected result is 111 and 333.
How can I accomplish this?
Regards
I think you want aggregation and having:
select order_id
from orders o
where code = 47
group by order_id
having min(user) <> max(user);
You can also express the having as:
having count(distinct user) >= 2
You can try below -
select order_id from tablename
group by order_id
having count(distinct user)>=1
You can do it via row_number() as well
Select distinct order_id from
(select order_id, code, row_number()
over
( Partition by order_id, code
Order by order_id, code) rn
from
tablename
where user in (1,2)
) where rn>=1
But I guess you already have a user column hence i dont think you require extra manipulation
Select orderid, code from table
Group by orderid, code having
max(distinct user) >=1

Top 2 Months of Sales by Customer - Oracle

I am trying to develop a query to pull out the top 2 months of sales by customer id. Here is a sample table:
Customer_ID Sales Amount Period
144567 40 2
234567 50 5
234567 40 7
144567 80 10
144567 48 2
234567 23 7
desired output would be
Customer_ID Sales Sum Period
144567 80 10
144567 48 2
234567 50 5
234567 40 7
I've tried
select sum(net_sales_usd_spot), valid_period, customer_id
from sales_trans_price_output
where valid_period in (select valid_period, sum(net_sales_usd_spot)
from sales_trans_price_output
where rank<=2)
group by valid_period, customer_id
error is
too many values ORA-00913.
I see why, but not sure how to rework it.
Try:
SELECT *
FROM (
SELECT t.*,
row_number() over (partition by customer_id order by sales_amount desc ) rn
FROM sales_trans_price t
)
WHERE rn <= 2
ORDER BY 1,2 desc
Demo: http://sqlfiddle.com/#!4/882888/3
what if you change your where clause to:
where valid_period in
(
select p.valid_period from sales_trans_price_output p
join (select valid_period, sum(net_sales_usd_spot)
from sales_trans_price_output
where rank<=2) s on s.valid_period = p.valid_period
)
It might be ugly and need refactoring, but I think this is the logic you're after.
The error is because of this.
where valid_period in (select valid_period, sum(net_sales_usd_spot)
from sales_trans_price_output
where rank<=2)
The subquery can only contain one field.
You are on the right track using rank, but you might not be using it correctly. Google oracle rank to find the correct syntax.
Back to what you are looking to achieve, a derived table is the approach I would use. That's simply a subquery with an alias. Or, if you use the keyword with, it might be called a CTE - Computed Table Expression.
Try it
SELECT * FROM (
SELECT T.*,
RANK () OVER (PARTITION BY CUSTOMER_ID
ORDER BY VALID_PERIOD DESC) FN_RANK
FROM SALES_TRANS_PRICE_OUTPUT T
) A
WHERE A.FN_RANK <= 2
ORDER BY CUSTOMER_ID ASC, VALID_PERIOD DESC, FN_RANK DESC

SQL server group by show which rows

I have a table called phonecalls which stores records of phone calls history from a company looks like this
ID Number Duration Cost
-------------------------------------
1 123456 13 1
2 222222 39 1
3 333333 69 2
4 222222 36 1
What I want to do is get sum of duration and cost for each number.
SELECT
Number,
SUM(Duration) as [Total_Duration],
SUM(Cost) as [Total_Cost]
FROM
phonecalls
GROUP BY
Number;
Now the catch is I also need which id did i included in each group by for auditing purpose so that others will know which rows are processed. So my question is how to you include ids when you do a group by?
Almighty Stackoverflow please help me.
Thanks in advance.
--EDIT:
Is it possible to get all ids with same number in one cell? something like
ID Number Total_Duration Total_Cost
---------------------------------------------------
1 123456 13 1
2,4 222222 75 2
3 333333 69 2
Hope im not asking for too much.
You're looking for the SUM OVER() function:
SQL Fiddle
SELECT
ID,
Number,
Total_Duration = SUM(Duration) OVER(PARTITION BY Number),
Total_Cost = SUM(Cost) OVER(PARTITION BY Number)
FROM phonecalls
If you want to concatenate the IDs, you can use FOR XML PATH(''):
SQL Fiddle
SELECT
ID = STUFF((
SELECT ',' + CONVERT(VARCHAR(10), ID)
FROM phonecalls
WHERE Number = p.Number
FOR XML PATH('')
), 1, 1, ''),
Number,
Total_Duration = SUM(Duration),
Total_Cost = SUM(Cost)
FROM phonecalls p
GROUP BY Number