I got (ERROR: more than one row returned by a subquery used as an expression) or incorrect count/sum inside subquery - sql

I would like to make a query that will show me the total count and sum of sales, and for 2 workers in the same rows.
When I'm not using group by in a subquery I get a count for total sales for this worker not counted for each month. But when I try to use Group By clause in a subquery it shows me this error:
PG::CardinalityViolation:
ERROR: more than one row returned by a subquery used as an expression
And that makes sense but how I can count and sum amount of sales per month for each worker? Is there any possibility of transferring the value of Group By clause from query to a subquery? Should I be using subquery for this specific issue or maybe I can somehow make it easier?
SELECT
DATE_PART('month', payment_date) as month,
COUNT(payment_id) AS total_count,
SUM(amount) AS total_amount,
(SELECT COUNT(payment_id)
FROM payment
WHERE staff_id = 1
GROUP BY DATE_PART('month', payment_date)) AS mike_count,
(SELECT SUM(amount)
FROM payment
WHERE staff_id = 1
GROUP BY DATE_PART('month', payment_date)) AS mike_amount,
(SELECT COUNT(payment_id)
FROM payment
WHERE staff_id = 2
GROUP BY DATE_PART('month', payment_date)) AS jon_count,
(SELECT SUM(amount)
FROM payment
WHERE staff_id = 2
GROUP BY DATE_PART('month', payment_date)) AS jon_amount
FROM
payment
GROUP BY
DATE_PART('month', payment_date)
ORDER BY
DATE_PART('month', payment_date);
This is the outcome w/o the group by clause in subqueries:
month |total_count |total_amount |mike_count |mike_amount |jon_count |jon_amount
2 |2016 |0.835184E4 |7292 |0.3025212E5 |7304 |0.3105992E5
3 |5644 |0.2388656E5 |7292 |0.3025212E5 |7304 |0.3105992E5
4 |6754 |0.2855946E5 |7292 |0.3025212E5 |7304 |0.3105992E5
5 |182 |0.51418E3 |7292 |0.3025212E5 |7304 |0.3105992E5

Use conditional aggregation instead:
SELECT DATE_PART('month', payment_date) as month,
count(payment_id) AS total_count,
SUM(amount) AS total_amount,
SUM(CASE WHEN staff_id = 1 THEN 1 ELSE 0 END) as mike_count,
SUM(CASE WHEN staff_id = 1 THEN amount ELSE 0 END) as mike_amount,
SUM(CASE WHEN staff_id = 2 THEN 1 ELSE 0 END) as jon_count,
SUM(CASE WHEN staff_id = 2 THEN amount ELSE 0 END) as jon_amount
FROM payment
GROUP BY DATE_PART('month', payment_date)
ORDER BY DATE_PART('month', payment_date);
DATE_PART() suggests that you are using Postgres. If so, I would recommend the FILTER clause:
SELECT DATE_PART('month', payment_date) as month,
COUNT(payment_id) AS total_count,
SUM(amount) AS total_amount,
COUNT(*) FILTER (WHERE staff_id = 1) as mike_count,
SUM(amount) FILTER (WHERE staff_id = 1) as mike_amount,
COUNT(*) FILTER (WHERE staff_id = 2) as jon_count,
SUM(amount) FILTER (WHERE staff_id = 2) as jon_amount
FROM payment
GROUP BY DATE_PART('month', payment_date)
ORDER BY DATE_PART('month', payment_date);

Related

ERROR: missing FROM-clause entry for table "p"

I am trying to use a UNION to append the total column in the first top half to the second half of the query.
SELECT NULL as month, NULL as active, count(cage_player_id) as total
FROM player_signup as p
GROUP BY date_part('month', p.signup_date)
UNION
SELECT date_part('month', signup_date) as month, count(DISTINCT(p.cage_player_id)) as active, NULL as total
FROM player_signup as p
JOIN daily_kpis as d ON p.cage_player_id = d.cage_player_id
WHERE slot_bet_amount > 0
OR ld_bet_amount > 0
OR table_bet_amount > 0
GROUP BY date_part('month', p.signup_date)
ORDER BY date_part('month', p.signup_date) ASC
I keep getting an error that says the FROM clause is missing for table p. Can anyone help? Is there an easier way to combine these two queries?
Here is what each query looks like separately. I just want to add the total column right next to the month and active column. The total ID's would still be broken down by months.
Query 1
Query 2
The problem is the order by clause. It applies to the results of the union query, so it cannot see the identifiers that are defined within the queries. Instead, you should use the names of the column in the resultset.
So you want to change this:
ORDER BY date_part('month', p.signup_date) ASC
To:
ORDER BY month
I believe this is the query you need:
SELECT date_part('month', signup_date) as month
, count(DISTINCT(p.cage_player_id)) as active
, (select count(cage_player_id)
FROM player_signup as p
GROUP BY date_part('month', p.signup_date)) as total
FROM player_signup as p
JOIN daily_kpis as d ON p.cage_player_id = d.cage_player_id
WHERE slot_bet_amount > 0
OR ld_bet_amount > 0
OR table_bet_amount > 0
GROUP BY date_part('month', p.signup_date)
ORDER BY date_part('month', p.signup_date) asc
I am not so good at Postgresql, please try this:
SELECT date_part('month', signup_date) as month
, count(DISTINCT(p.cage_player_id)) as active
, max(t1.total) as total
FROM player_signup as p
left join (select count(pp.cage_player_id) over (partition by date_part('month', pp.signup_date)) as total
, date_part('month', pp.signup_date) date_p
FROM player_signup as pp) t1 on t1.date_p = date_part('month', p.signup_date)
JOIN daily_kpis as d ON p.cage_player_id = d.cage_player_id
WHERE slot_bet_amount > 0
OR ld_bet_amount > 0
OR table_bet_amount > 0
GROUP BY date_part('month', p.signup_date)
ORDER BY date_part('month', p.signup_date)

Finding least and highest month revenue

I was having a problem for organizing a table where shows me
the month with the highest revenue and the lowest order by revenue
all the information is in the same table (Ordertable)
So I have orderdate and orderfinalprice.
select orderdate as MonthsSales, Highestrevenue, Lowestrevenue
from
(select months
, max (orderfinalprice) as Highestrevenue, min (orderfinalprice) as Lowestrevenue
From hologicOrder_T)
order by monthsales;
What's missing is the grouping by month based on orderdate.
select orderdate as MonthsSales, Highestrevenue, Lowestrevenue
from
(select to_char(orderdate, 'Month') orderdate, max(orderfinalprice) as Highestrevenue
, min (orderfinalprice) as Lowestrevenue
from hologicOrder_T
group by to_char(orderdate, 'Month'))
order by monthsales;
As far as I understood your problem, you want two months data, one with highest revenue and one with lowest revenue.
You can get it by following:
Select max(case when minrn = 1 then month_ end) lowestrevenue_month,
max(case when minrn = 1 then totalrevenue end) lowestrevenue,
max(case when maxrn = 1 then month_ end) highestrevenue_month,
max(case when maxrn = 1 then totalrevenue end) highestrevenue
From
(Select trunc(orderdate, 'month') month_,
Sum(orderfinalprice) as totalrevenue,
Row_nuumber() over (partition by trunc(orderdate, 'month') order by Sum(orderfinalprice)) as minrn,
Row_nuumber() over (partition by trunc(orderdate, 'month') order by Sum(orderfinalprice) desc) as maxrn
From hologicOrder_T
Group by trunc(orderdate, 'month') )
Where 1 in (minrn, maxrn);
You should not use to_char(orderdate, 'Month') because it will be same for the month regardless of its year.
In output, highestrevenue_month and lowestrevenue_month will be first date of the month, you can format it accordingly using to_char.
Cheers!!

SQL - CASE WHEN query

I am trying to get from my table all invoices raised for those customers who spent more than 1000 over period of last 12 months. Below is my table just for two customers as example:
And my query:
SELECT
t.Customer, t.Invoice
FROM
(SELECT
CI.Customer, CI.Invoice, CI.Date,
SUM(CASE
WHEN CI.Date > DATEADD(month, -12, getdate())
THEN CI.Valuee
ELSE 0
END) as Net
FROM
CustomerInvoice CI
GROUP BY
CI.Customer, CISRV.Invoice, CISRV.Date) AS t
GROUP BY
t.Customer, t.Invoice
HAVING
SUM (t.Net) > 1000
As result I will get only invoice INV-341453 but I would like to show also invoices INV-346218 and INV-349065.
What I am doing wrong?
Use ANSI standard window functions:
select ci.*
from (select ci.*,
sum(ci.value) over (partition by ci.customer) as total_value
from CustomerInvoice CI
where CI.Date > DATEADD(month, -12, getdate())
) ci
where total_value > 1000;
By "all invoices", I assume you mean the ones in the past twelve months.
You could do this by using a grouped query to identify the customers who have gone over the 1000 threshold, and then display all invoices for those customers:
SELECT Customer, Invoice
FROM CustomerInvoice
WHERE Customer IN
(SELECT Customer
FROM CustomerInvoice
GROUP BY Customer
HAVING SUM(CASE WHEN CI.Date>DATEADD(month,-12,getdate()) THEN CI.Valuee ELSE 0 END) > 1000)

Loop in Oracle SQL, comparing one month to another

I have to draft a SQL query which does the following:
Compare current week (e.g. week 10) amount to the average amount over previous 4 weeks (Week# 9,8,7,6).
Now I need to run the query on a monthly basis so say for weeks (10,11,12,13).
As of now I am running it four times giving the week parameter on each run.
For example my current query is something like this :
select account_id, curr.amount,hist.AVG_Amt
from
(
select
to_char(run_date,'IW') Week_ID,
sum(amount) Amount,
account_id
from Transaction t
where to_char(run_date,'IW') = '10'
group by account_id,to_char(run_date,'IW')
) curr,
(
select account_id,
sum(amount) / count(to_char(run_date,'IW')) as AVG_Amt
from Transactions
where to_char(run_date,'IW') in ('6','7','8','9')
group by account_id
) hist
where
hist.account_id = curr.account_id
and curr.amount > 2*hist.AVG_Amt;
As you can see, if I have to run the above query for week 11,12,13 I have to run it three separate times. Is there a way to consolidate or structure the query such that I only run once and I get the comparison data all together?
Just an additional info, I need to export the data to Excel (which I do after running query on the PL/SQL developer) and export to Excel.
Thanks!
-Abhi
You can use a correlated sub-query to get the sum of amounts for the last 4 weeks for a given week.
select
to_char(run_date,'IW') Week_ID,
sum(amount) curAmount,
(select sum(amount)/4.0 from transaction
where account_id = t.account_id
and to_char(run_date,'IW') between to_char(t.run_date,'IW')-4
and to_char(t.run_date,'IW')-1
) hist_amount,
account_id
from Transaction t
where to_char(run_date,'IW') in ('10','11','12','13')
group by account_id,to_char(run_date,'IW')
Edit: Based on OP's comment on the performance of the query above, this can also be accomplished using lag to get the previous row's value. Count of number of records present in the last 4 weeks can be achieved using a case expression.
with sum_amounts as
(select to_char(run_date,'IW') wk, sum(amount) amount, account_id
from Transaction
group by account_id, to_char(run_date,'IW')
)
select wk, account_id, amount,
1.0 * (lag(amount,1,0) over (order by wk) + lag(amount,2,0) over (order by wk) +
lag(amount,3,0) over (order by wk) + lag(amount,4,0) over (order by wk))
/ case when lag(amount,1,0) over (order by wk) <> 0 then 1 else 0 end +
case when lag(amount,2,0) over (order by wk) <> 0 then 1 else 0 end +
case when lag(amount,3,0) over (order by wk) <> 0 then 1 else 0 end +
case when lag(amount,4,0) over (order by wk) <> 0 then 1 else 0 end
as hist_avg_amount
from sum_amounts
I think that is what you are looking for:
with lagt as (select to_char(run_date,'IW') Week_ID, sum(amount) Amount, account_id
from Transaction t
group by account_id, to_char(run_date,'IW'))
select Week_ID, account_id, amount,
(lag(amount,1,0) over (order by week) + lag(amount,2,0) over (order by week) +
lag(amount,3,0) over (order by week) + lag(amount,4,0) over (order by week)) / 4 as average
from lagt;

Need to add total # of orders to summary query

In the following, I need to add here the total of orders per order type which is IHORDT. I tried count(t01.ihordt), but its not a valid. I need this order total to get average amount per order.
Data expected:
Current:
IHORDT current year previous year
RTR 100,000 90,000
INT 2,000,000 1,500,000
New change: add to the above one column:
Total orders
RTR 100
INT 1000
SELECT T01.IHORDT
-- summarize by current year and previous year
,SUM( CASE WHEN YEAR(IHDOCD) = YEAR(CURRENT TIMESTAMP) - 1
THEN (T02.IDSHP#*T02.IDNTU$) ELSE 0 END) AS LastYear
,SUM( CASE WHEN YEAR(IHDOCD) = YEAR(CURRENT TIMESTAMP)
THEN (T02.IDSHP#*T02.IDNTU$) ELSE 0 END) AS CurYear
FROM ASTDTA.OEINHDIH
T01 INNER JOIN
ASTDTA.OEINDLID T02
ON T01.IHORD# = T02.IDORD#
WHERE T01.IHORDT in ('RTR', 'INT')
--------------------------------------------------------
AND ( YEAR(IHDOCD) = YEAR(CURRENT TIMESTAMP) - 1
OR YEAR(IHDOCD) = YEAR(CURRENT TIMESTAMP))
GROUP BY T01.IHORDT
To receive a count of records in a group you need to use count(*).
So here is a generic example:
select order_type,
sum(order_amount) as total_sales,
count(*) as number_of_orders
from order_header
group by order_type;