GROUP BY select within SELECT - mysql

I have one table which i want to select some rows and group them by date. the problem is when i'm selecting within my select that row appear to not grouped by date, see the example:
SELECT date, SUM(price), (SELECT SUM(price), date FROM expense WHERE
user = 'Catherine') as cprice FROM expense GROUP BY date
it does not grouping cprice, it will sum all Catherine prices and doesn't group it by date, by the way I tried GROUP BY date into the within select but doesn't work either.Any suggestion?
expense table:
id user price date
1 Frank 20 2014
2 Catherine 10 2013
3 Catherine 20 2014
thank you

You can try this:
SELECT
date,
SUM(price) as totalPrice,
SUM(IF(user='Catherine', price, 0)) as cprice
FROM expense
GROUP BY date
If you want both price together then you can try this:
SELECT
date,
SUM(price) + SUM(IF(user='Catherine', price, 0)) as cprice
FROM expense
GROUP BY date

try this query, it select * row where user = 'Catherine' and groups them by date
SELECT
date,
SUM(price)
FROM expense
WHERE user = 'Catherine'
GROUP BY date

Try Using Case Statement:(I'm Not Familiar With MySQL :( )
Not Sure,Like:
SELECT
date,
SUM(price) as totalPrice,
SUM(Case When user='Catherine' Then price Else 0 End CASE;) as catherine_price
FROM expense
GROUP BY date

You probaby can solve it using a two selects and pull out the data you need using a loop.

Related

Getting the number of users for this year and last year in SQL

My table is like this:
root_tstamp
userId
2022-01-26T00:13:24.725+00:00
d2212
2022-01-26T00:13:24.669+00:00
ad323
2022-01-26T00:13:24.629+00:00
adfae
2022-01-26T00:13:24.573+00:00
adfa3
2022-01-26T00:13:24.552+00:00
adfef
...
...
2021-01-26T00:12:24.725+00:00
d2212
2021-01-26T00:15:24.669+00:00
daddfe
2021-01-26T00:14:24.629+00:00
adfda
2021-01-26T00:12:24.573+00:00
466eff
2021-01-26T00:12:24.552+00:00
adfafe
I want to get the number of users in the current year and in previous year like below using SQL.
Date Users previous_year
2022-01-01 10 5
2022-01-02 20 15
The code is written as follows.
select CAST(root_tstamp as DATE) as Date,
count(DISTINCT userid) as users,
count(Distinct case when CAST(root_tstamp as DATE) = dateadd(MONTH,-12,CAST(root_tstamp as DATE)) then userid end) as previous_year
FROM table1
But it returns 0 for previous_year values.
How can I fix that?
Possible solution for SQL Server:
WITH cte AS ( SELECT 2022 [year]
UNION ALL
SELECT 2021 )
SELECT cte.[year],
COUNT(DISTINCT test.userId) current_users_amount,
COUNT(DISTINCT CASE WHEN YEAR(test.root_tstamp) < cte.[year]
THEN test.userId
END) previous_users_amount
FROM test
JOIN cte ON YEAR(test.root_tstamp) <= cte.[year]
GROUP BY cte.[year]
https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=88b78aad9acd965bdbac4c85a0b81927
This query (for MySql) returns unique number of userids where the root_timestamp is in the current year, by day, and the number of unique userids for the same day last year. If there is no record for a day in the current year nothing will be displayed for that day. If there are rows for the current year, but no rows for the same day last year, then NULL will be shown for that lastyear column.
SELECT cast(ty.root_tstamp as date) as Dte,
COUNT(DISTINCT ty.userId) as users_this_day,
count(distinct lysd.userid) as users_sameday_lastyear
FROM test ty
left join
test lysd
on cast(lysd.root_tstamp as date)=date_add(cast(ty.root_tstamp as date), interval -1 year)
WHERE YEAR(ty.root_tstamp) = year(current_date())
GROUP BY Dte
If you wish to show output rows for calendar days even if there are no rows in current year and/or last year, then you also need a calendar table to be introduced (let's hope that it is not what you need)

Way to do MAX(evaluation_expression, return_expression) in SQL

I find myself often wanting to get an adjacent row value when I do a MIN or MAX statement. For example in the following statement:
WITH people AS (
select 'Greg' as name, 20 as age union
select 'Tom' as name, 17 as age
) SELECT MAX(age) FROM people;
# MAX(age)
20
The MAX function does the equivalent of: MAX(eval_expression=age, return_expression=age), where it always has the same evaluation and return value (implicitly). However, I would like to find the name of the person with the max age. So, the conceptual syntax would be: MAX(eval_expression=age, return_expression=name). This is a pattern I find myself using quite frequently and usually end up hacking something together like:
WITH people AS (
select 'Greg' as name, 20 as age union
select 'Tom' as name, 17 as age
) SELECT name FROM people NATURAL JOIN (SELECT name, MAX(age) age FROM people) _;
# name
'Greg'
Is there a generic way to do the MAX(expr, return) that I'm trying to accomplish?
Update: to provide an example where an aggregation is required:
with sales as (
select DATE '2014-01-01' as date, 100 as sales, 'Fish' as product union
select DATE '2014-01-01' as date, 105 as sales, 'Potatoes' as product union
select DATE '2014-01-02' as date, 84 as sales, 'Salsa' as product
) select date, max(sales) from sales group by date
# date, max(sales)
2014-01-01, 105
2014-01-02, 84
And how to get the equivalent of: MAX(expr=sales, return=product)? Something like:
WITH sales AS (
select DATE '2014-01-01' as d, 100 as revenue, 'Fish' as product union
select DATE '2014-01-01' as d, 105 as revenue, 'Potatoes' as product union
select DATE '2014-01-02' as d, 84 as revenue, 'Salsa' as product
) SELECT d AS date, product FROM sales NATURAL JOIN (SELECT d, MAX(revenue) AS revenue FROM sales GROUP BY d) _;
# date, product
2014-01-01, Potatoes
2014-01-02, Salsa
Unless I'm missing something here -
use limit with order by:
WITH people AS (
select 'Greg' as name, 20 as age union
select 'Tom' as name, 17 as age
)
SELECT name
FROM people
ORDER BY age DESC
LIMIT 1;
# name
'Greg'
If you want to use first_value(), I would recommend:
select distinct date,
first_value(product) over(partition by date order by sales desc) top_product
from sales
No need for aggregation here, nor for a frame specification in the window function. The window function walks the dataset starting from the row with the greatst sales, so all rows in the partition get the same top_product assigned. Then distinct retains only one row per partition.
But basically, this ends up as a greatest-n-per group problem, where you want the row with the greatest sale for each date. The first_value() solution does not scale well if you want more than one column on that row. A typical solution is to rank records in a subquery, then filter. Again, no aggregation is needed, that's filtering logic only:
select *
from (
select s.*
row_number() over(partition by date order bys ales desc) rn
from sales
) t
where rn = 1
One solution would be to use an unbounded window function such as FIRST_VALUE, where you can sort the date partition by sales. Here would be an example:
;WITH sales AS (
select DATE '2014-01-01' as date, 100 as sales, 'Fish' as product union
select DATE '2014-01-01' as date, 105 as sales, 'Potatoes' as product union
select DATE '2014-01-01' as date, 103 as sales, 'Lettuce' as product union
select DATE '2014-01-02' as date, 84 as sales, 'Salsa' as product
)
SELECT DISTINCT date, LAST_VALUE(product) OVER (
partition by date
order by sales
-- Default: https://dev.mysql.com/doc/refman/8.0/en/window-functions-frames.html
-- rows between unbounded preceding and current row
rows between unbounded preceding and unbounded following
) top_product
FROM sales group by date;
# date, top_product
'2014-01-01', 'Potatoes'
'2014-01-02', 'Salsa'
I think the subselect might be easier to read (at least for me), but this is another option. You'd have to check on the performance of the two but I'd think the analytic function (without the not-indexeable join) would be much faster.

How to get rows with max date when grouping in MySQL?

I have a table with prices and dates on product:
id
product
price
date
I create a new record when price change. And I have a table like this:
id product price date
1 1 10 2014-01-01
2 1 20 2014-02-17
3 1 5 2014-03-28
4 2 25 2014-01-05
5 2 12 2014-02-08
6 2 30 2014-03-12
I want to get last price for all products. But when I group with "product", I can't get a price from a row with maximum date.
I can use MAX(), MIN() or COUNT() function in request, but I need a result based on other value.
I want something like this in final:
product price date
1 5 2014-03-28
2 30 2014-03-12
But I don't know how. May be like this:
SELECT product, {price with max date}, {max date}
FROM table
GROUP BY product
Alternatively, you can have subquery to get the latest get for every product and join the result on the table itself to get the other columns.
SELECT a.*
FROM tableName a
INNER JOIN
(
SELECT product, MAX(date) mxdate
FROM tableName
GROUP BY product
) b ON a.product = b.product
AND a.date = b.mxdate
I think the easiest way is a substring_index()/group_concat() trick:
SELECT product,
substring_index(group_concat(price order by date desc), ',', 1) as PriceOnMaxDate
max(date)
FROM table
GROUP BY product;
Another way, that might be more efficient than a group by is:
select p.*
from table t
where not exists (select 1
from table t2
where t2.product = t.product and
t2.date > t.date
);
This says: "Get me all rows from the table where the same product does not have a larger date." That is a fancy way of saying "get me the row with the maximum date for each product."
Note that there is a subtle difference: the second form will return all rows that on the maximum date, if there are duplicates.
Also, for performance an index on table(product, date) is recommended.
You can use a subquery that groups by product and return the maximum date for every product, and join this subquery back to the products table:
SELECT
p.product,
p.price,
p.date
FROM
products p INNER JOIN (
SELECT
product,
MAX(date) AS max_date
FROM
products
GROUP BY
product) m
ON p.product = m.product AND p.date = m.max_date
SELECT
product,
price,
date
FROM
(SELECT
product,
price,
date
FROM table_name ORDER BY date DESC) AS t1
GROUP BY product;

SQL query to return amount of sales and amount of salespersons

4am here... this is driving me nuts. I have a report table:
id_report
invoice_date
id_user (sales person)
I need to display, by month, how many items were sold, and also how many salespersons sold those items. For example, in january user 3 sold 10 items and user 8 sold 7 items, that should return:
date | items | salespersons
2014-01 | 17 | 2
This was my first approach, but that doesn't bring me the 3rd column:
SELECT DATE_FORMAT(invoice_date, "%Y-%m") AS date, COUNT(*) AS items,
FROM report
GROUP BY date
Thanks!
In your query you haven't add the third column. Try this:
SELECT DATE_FORMAT(invoice_date, "%Y-%m") AS date,
COUNT(*) AS items,
COUNT(DISTINCT id_user) AS salespersons
FROM report
GROUP BY date
Working demo: http://sqlfiddle.com/#!2/03e45/1
It's important to use the DISTINCT keyword, or you will have the same count as items.
SELECT
DATE_FORMAT(invoice_date, '%Y-%m') AS date,
COUNT(*) AS items,
COUNT(DISTINCT id_user) as sales_persons
FROM report
GROUP BY date
You need to add count( id_user ) as sales_persons.
This is not being selected.
SELECT
DATE_FORMAT(invoice_date, "%Y-%m") AS date,
COUNT(*) AS items,
count( id_user ) as sales_persons
FROM report
GROUP BY date
You are not asking for the third column in your query. Add COUNT(id_user) as salespersons. Do your query like this:
SELECT DATE_FORMAT(invoice_date, "%Y-%m") AS date, COUNT(*) AS items, COUNT(id_user) as salespersons FROM report GROUP BY date

SQL query: calculate profile or loss in given 2 tables

I have 2 tables storing income:
id name date price isDel
1 abc 2012-01-26 613.90 0
2 cde 2012-01-25 530.10 0
and expenses:
id name price date isDel
1 b1 334.90 2012-01-26 0
2 b1 41.90 2012-01-25 0
3 d1 61.90 2012-01-25 0
I would like to get the result as follows:
date total income total expense profile n loss
2012-01-25 530.10 103.80 426.30
2012-01-26 613.90 344.90 26.90
Thanks. Can anyone provide SQL solutions? I did refer some member's question and answer at stackoverflow but I can't modify to want I need. Please help.
Provided you want profit/loss per day this should do:
SELECT Inc.date, TotalInc, TotalExp, TotalInc - TotalExp AS ProfitNLoss
FROM
(
SELECT date, SUM(price) as TotalInc
FROM Income
WHERE isdel = 0
GROUP BY date) as Inc
LEFT OUTER JOIN
(
SELECT date, SUM(price) as TotalExp
FROM Expense
WHERE isdel = 0
GROUP BY date) as Exp
ON Inc.date = Exp.date
UNION
SELECT Exp.date, TotalInc, TotalExp, TotalInc - TotalExp AS ProfitNLoss
FROM
(
SELECT date, SUM(price) as TotalInc
FROM Income
WHERE isdel = 0
GROUP BY date) as Inc
RIGHT OUTER JOIN
(
SELECT date, SUM(price) as TotalExp
FROM Expense
WHERE isdel = 0
GROUP BY date) as Exp
ON Inc.date = Exp.date
Argh, this is "give me the codes" question, but I'll give you few hints:
mySQL GROUP BY tutorial
mySQL GRUOP BY aggregate functions
mySQL subqueries
So what you need to do:
Create list of all unique dates (unfortunately I haven't find a way to generate list of dates within sql without table query or without long php array, if anyone knows a way to do this I'll be glad to add it to anser), so you will have to do this:
SELECT date FROM income GROUP BY date;
-- If there's a chance that you'll have record in expenses and not in income:
(SELECT date FROM income GROUP BY date)
UNION (SELECT date FROM expenses GROUP BY date;)
GROUP BY date;
-- Or create table containing just list of all dates (best option)
And than join all unique dates with expenses and income:
SELECT SUM(expenses.price) AS expenses, SUM(incomes.price) AS income, dates.date
FROM unique_dates -- or from your subquery
LEFT JOIN incomes ON incomes.date = unique_dates.date
LEFT JOIN expenses ON expenses.date = unique_dates.date
GROUP BY unique_dates.date
Oh and I have no idea what does profile n loss mean.
Thanks to Pheiberg and appreciated for your prof reply.
I just did some little change so it will return 0 when table has no record or return NULL, So that Profit calculation still able to cal even given -ve value. Here the code share to u guys.
note: im using MySQL
SELECT Inc.create_dt, IFNULL(TotalInc,0), IFNULL(TotalExp,0), IFNULL(TotalInc,0) - IFNULL(TotalExp,0) AS ProfitNLoss
FROM
(
SELECT create_dt, SUM(price) as TotalInc
FROM income
WHERE IsDel= 0
GROUP BY create_dt) as Inc
LEFT OUTER JOIN
(
SELECT create_dt, SUM(price) as TotalExp
FROM expenses
WHERE IsDel= 0
GROUP BY create_dt) as Exp
ON Inc.create_dt= Exp.create_dt
UNION
SELECT Exp.create_dt, IFNULL(TotalInc,0), IFNULL(TotalExp,0), IFNULL(TotalInc,0) - IFNULL(TotalExp,0) AS ProfitNLoss
FROM
(
SELECT create_dt, SUM(price) as TotalInc
FROM income
WHERE IsDel= 0
GROUP BY create_dt) as Inc
RIGHT OUTER JOIN
(
SELECT create_dt, SUM(price) as TotalExp
FROM expenses
WHERE IsDel= 0
GROUP BY create_dt) as Exp
ON Inc.create_dt= Exp.create_dt