Dont select data from database with two tables using mysql - mysql

I have 2 tables sale and receipt.Table structure and result structure is shown below.
sale
date total sale_type
15-8-2014 50 credit
16-8-2014 100 credit
17-8-201 200 return
18-8-2014 300 return
receipt
date net_amount
15-8-2014 100
16-8-2014 200
17-8-2014 300
result
date sale receipt
15-8-2014 50 100
16-8-2014 100 200
17 -8-2014 200 300
18-8-2014 300
Using my query i got these result structure ,but i want to get also the sum total in the case sale_type='credit' and sale_type='return".Any body help me?
My query is
select date,total,net_amount from
(select date, total, null as net_amount, 2 as sort_col from sale union
all select date,
null as total, net_amount as net_amount, 1 as sort_col from receipt)
as a order by date desc, sort_col desc

Does the following query (using JOIN and CASE get you expected results?
SELECT
s.date,
s.total AS sale,
r.net_amount AS receipt,
SUM(CASE
WHEN s.sale_type = 'credit' THEN s.total
ELSE 0
END) AS sum_credit,
SUM(CASE
WHEN s.sale_type = 'return' THEN s.total
ELSE 0
END) AS sum_return
FROM sale s
LEFT JOIN receipt r
ON s.date = r.date
GROUP BY s.date, s.total, r.net_amount;

Related

How to add a few restrictios to a query?

I have difficulty with syntax...
This is my query:
SELECT t.diapason,
Count(*) AS 'number_of_users'
FROM (SELECT CASE
WHEN amount < 200 THEN '0-200'
WHEN amount >= 200 THEN '200 +'
end AS diapason
FROM (SELECT Sum(amount) AS amount
FROM payments) p) t
GROUP BY t.diapason
ORDER BY number_of_users DESC;
But now I need to select only users which had activity.login_time between '2018-01-01' and'2018-01-12'.
So, I think I should use INNER JOIN and set period of time. Bu how?
My tables:
activity
user_id login_time
1 01.01.2018
2 01.01.2018
3 03.01.2018
4 30.02.2018
payments
user_id amount payment_time
1 50 10.12.2017
1 200 09.12.2017
2 40 08.08.2017
what should I change in my query to add activity.login_time?
Output for period 01.01.2018-12.01.2018
diapason number_of_users
0-200 2
200+ 1
I understand your question as this. You had 3 users (user_id=1,2,3) login in the period 01.01.2018-12.01.2018. Of those users, user_id 1 made 2 payments totalling 250, user_id 2 made 1 payment of 40, and user_id 3 made 0 payments so their total is 0. Hence there are 2 values in the range 0-200, and 1 in the range 200 +. If that is the correct understanding, this query will give you the desired results:
SELECT CASE
WHEN amount < 200 THEN '0-200'
WHEN amount >= 200 THEN '200 +'
END AS diapason,
COUNT(*) AS number_of_users
FROM (SELECT a.user_id, COALESCE(SUM(p.amount), 0) AS amount
FROM activity a
LEFT JOIN payments p ON p.user_id = a.user_id
WHERE a.login_time BETWEEN '01.01.2018' AND '12.01.2018'
GROUP BY a.user_id) p
GROUP BY diapason;
Output:
diapason number_of_users
0-200 2
200 + 1
SQLFiddle demo
Update
To add another row with the total number_of_users, just add WITH ROLLUP to the GROUP BY clause:
SELECT CASE
WHEN amount < 200 THEN '0-200'
WHEN amount >= 200 THEN '200 +'
END AS diapason,
COUNT(*) AS number_of_users
FROM (SELECT a.user_id, COALESCE(SUM(p.amount), 0) AS amount
FROM activity a
LEFT JOIN payments p ON p.user_id = a.user_id
WHERE a.login_time BETWEEN '01.01.2018' AND '12.01.2018'
GROUP BY a.user_id) p
GROUP BY diapason WITH ROLLUP
Output:
diapason number_of_users
0-200 2
200 + 1
(null) 3
In your application framework you can use the fact that the diapason value is NULL to output something like Total instead.
Updated SQLFiddle
You can also do the same in MySQL (see this SQLFiddle) by wrapping this query up as a subquery and using a COALESCE on the diapason column. In that case the output would be:
diapason number_of_users
0-200 2
200 + 1
Total 3
You add WHERE clause to filter.
SELECT t.diapason,
COUNT(*) AS 'number_of_users'
FROM (
SELECT
CASE
WHEN amount < 200 THEN '0-200'
WHEN amount >= 200 THEN '200 +'
END AS diapason
FROM (
SELECT payments.user_id, SUM(amount) AS amount
FROM payments
INNER JOIN activity ON payments.user_id = activity.user_idAND activity.login_time = payments.payment_time
WHERE activity.login_time BETWEEN '2018-01-10' AND '2018-01-12'
GROUP BY payments.user_id
) p
) t
GROUP BY t.diapason
ORDER BY number_of_users DESC;

About correct use of sum in case statement

There is a table "payments"
user_id payment_time amount sale_type
1 2018-04-01 10 cash
1 2018-04-01 10 cash
1 2018-04-01 10 cash
1 2018-04-01 20 bank
2 2018-04-01 10 cash
2 2018-04-01 10 cash
Need the sum of cash.
I don't understand why this query gives wrong results:
select SUM(CASE WHEN p1.sale_type='cash' THEN p1.amount ELSE 0 END)
as cash
FROM
(SELECT distinct user_id, SUM(amount) AS amount, sale_type FROM payments where
payment_time = '2018-04-01' group by user_id) p1
You need to add sale_type column to GROUP BY statement for the inner query and that should be group by user_id, sale_type for the correct results for your query style.
P.S. actually, I don't think you need a subquery.
The above query gives result as 60, while
select SUM(CASE WHEN p1.sale_type='cash' THEN p1.amount ELSE 0 END) as cash
from
(select distinct user_id, SUM(amount) AS amount, sale_type
from payments
where payment_time = date'2018-04-01'
group by user_id, sale_type) p1;
or
select SUM(CASE WHEN sale_type='cash' THEN amount ELSE 0 END) as cash
from payments
where payment_time = date'2018-04-01'
gives 40 for resulting cash column
SQL Fiddle Demo
Why don't you use 'Having' clause which is made for this purpose.
SELECT SUM(amount) AS cash FROM payments
WHERE payment_time = '2018-04-01'
GROUP BY sale_type
HAVING sale_type= 'cash'
I think you may not need the distinct in your sub-query or the entire sub-query at all.
select p.user_id as id, sum(case when p.sale_type = 'cash' then p.amount else 0 end) as amount
from payments p
where p.payment_time = '2018-04-01'
group by p.user_id
or without case
select p.user_id, sum(p.amount)
from payments p
where p.sale_type = 'cash' and p.payment_time = '2018-04-01'
group by p.user_id

How to divide the result of calculation on groups?

Good day! Could you help me with query?
I have a table "payments":
payments
user_id amount payment_time sale_type
1 20 31.01.2011 card
1 10 02.01.2012 cash
3 10 03.01.2012 card
4 15 05.02.2012 cash
...and so on
The task is to select total amount of payments for 01.01.2012 - 30.01.2012 and divide this sum on groups due to the amount user ever payed.
The groups are "0-10" - if sum is 0 -10 $
"10 and more" - if sum > 10 $.
My code:
SELECT * from (select IFnull(t.diapason,'total') as diapason, total_amount
FROM
(SELECT p.user_id, p.amount as total_amount, CASE
when amount<=10 then '0-10'
when amount>10 then '10 and more' END AS diapason
FROM (SELECT distinct payments.user_id, SUM(amount) AS amount
FROM payments inner JOIN (SELECT DISTINCT user_id
FROM payments where payment_time between '2012-01-01'
and '2012-01-30') a ON payments.user_id = a.user_id
GROUP BY payments.user_id) p) t GROUP BY diapason WITH ROLLUP) as
t1 ORDER BY total_amount desc;
What is wrong here?
Expected output
diapason total_amount
0-10 10 - here is user with id 3
10 and more 10 - here is user with id 1 (because he ever payed 30)
total
Try this query -
select case when p2.amount <=10 then '0-10'
else '10 and more' end diapason
,p1.amount "total amount"
,p1.payment_by_card
,p1.cash
from (select user_id, sum(amount) amount, payment_by_card, cash
from payments
where payment_time between '2012-01-01' and '2012-01-30'
group by user_id, payment_by_card, cash) p1
join (select user_id, sum(amount) amount
from payments
group by user_id) p2
on p1.user_id = p2.user_id
Here is the fiddle - http://www.sqlfiddle.com/#!9/22caaa/8

mysql subquery for charts

I have 2 tables with the following data
inviteTable
inviteDate | contractAmount | status
2014-01-01 1500 awarded
2015-01-01 2000 awarded
2015-01-02 4000 closed
2015-02-01 6000 awarded
2015-02-02 8000 awarded
2015-03-01 8500 awarded
quoteTable
quoteDate | quoteAmount | status
2014-03-01 1500 awarded
2015-01-02 2000 awarded
2015-01-03 4000 sent
2015-01-04 6000 awarded
2015-02-03 8000 awarded
2015-02-10 8500 sent
2015-02-11 9000 awarded
2015-03-01 9500 awarded
I want to make ONE query that gives me the following data structure
month | quotedTotal | quotedCount | awardedTotal | awardedCount
January 8000 2 2000 1
February 17000 2 14000 2
March 9500 1 8500 1
currently i have written this code...but it doesn't work
SELECT
MONTHNAME(t1.due_date) as month,
(select sum(`amount`) from quoteTable where awarded= 1 ) as estimatedAmount,
sum(t1.contactAmount) AS sumContactAmount,
COUNT(*) as `noOfAwarded`
FROM inviteTable t1
where
t1.status = "Awarded" and
t1.inviteDate between '2014-11-01' and '2015-10-31'
GROUP BY MONTH(due_date)
;
basically I want to have ONE query that gives me the following:
volume that got awarded - sum of contractAmount and status awarded
count of how many awarded - count of inviteTable.status=awarded
volume that got quoted - sum of quoteAmount and status awarded
count of how many quoted - count of quotedTable.status=awarded
all grouped by month and in a date range
my query doesn't work. Your help would be highly appreciated as I am stuck!
Your schema is unfortunate; it would be easier if you have just one table with an extra column to record what type of record you have. However, if you first collect the two tables into one table via union all, you can use a fairly simple query to produce the results to want:
select
monthname(_date) month,
sum(quoteAwarded * amount) quotedTotal,
sum(quoteAwarded) quotedCount,
sum(contractAwarded * amount) awardedTotal,
sum(contractAwarded) awardedCount
from (select inviteDate _date, contractAmount amount, status = 'awarded' contractAwarded, 0 quoteAwarded from inviteTable
union all
select quoteDate, quoteAmount, 0, status = 'awarded' from quoteTable) x
where _date between '2014-11-01' and '2015-10-31'
group by 1
order by month(_date)
See SQLFiddle using your sample data, and producing you expected output.
At the core of this query is the convenient fact that (in mysql) "true" is 1 and "false" is 0 - allowing the use of sum() instead of count(), and more importantly avoiding the use of case by using sum() over some simple math.
Here is a way you can do it, note that you have dates in both tables and you need to use join preferably left join , so this will ensure that the dates present in the left table is always returned in this case the month name.
select
t1.month,
t1.awardedTotal,
t1.awardedCount,
t2.quotedTotal,
t2.quotedCount
from(
select
monthname(t1.inviteDate) as month,
month(t1.inviteDate) as m,
sum(
case
when t1.status = 'awarded' then t1.contractAmount else 0
end
) as awardedTotal,
sum(
case
when t1.status = 'awarded' then 1 else 0
end
) as awardedCount
from inviteTable t1
where t1.inviteDate between '2014-11-01' and '2015-10-31'
group by month
)t1
left join(
select
monthname(t2.quoteDate) as month,
sum(
case
when t2.status = 'awarded' then t2.quoteAmount else 0
end
) as quotedTotal,
sum(
case
when t2.status = 'awarded' then 1 else 0
end
) as quotedCount
from quoteTable t2
where t2.quoteDate between '2014-11-01' and '2015-10-31'
group by month
)t2 on
t1.month = t2.month
order by t1.m
http://sqlfiddle.com/#!9/a863a/10
UPDATE:
Yes since you want dates present in both tables it can not be done with the above process since it always consider one table as left table and will get all the dates from that table. For getting data for all the dates across both tables you first need to get the dates from both tables and then using left join get the calculation part something as
select
t1.month,
coalesce(t2.awardedTotal,0) as awardedTotal,
coalesce(t2.awardedCount,0) as awardedCount,
coalesce(t3.quotedTotal,0) as quotedTotal,
coalesce(t3.quotedCount,0) as quotedCount
from(
select month(inviteDate) as m,monthname(inviteDate) as month
from inviteTable where inviteDate between '2014-11-01' and '2015-10-31'
union
select month(quoteDate) as m,monthname(quoteDate) as month
from quoteTable where quoteDate between '2014-11-01' and '2015-10-31'
)t1
left join(
select
monthname(inviteDate) as month,
sum(
case
when status = 'awarded' then contractAmount else 0
end
) as awardedTotal,
sum(
case
when status = 'awarded' then 1 else 0
end
) as awardedCount
from inviteTable
group by month
)t2 on t1.month = t2.month
left join(
select
monthname(quoteDate) as month,
sum(
case
when status = 'awarded' then quoteAmount else 0
end
) as quotedTotal,
sum(
case
when status = 'awarded' then 1 else 0
end
) as quotedCount
from quoteTable
group by month
)t3 on t1.month=t3.month
order by t1.m
http://sqlfiddle.com/#!9/ddaac/14

Grouping items between 2 numbers

I have a query that looks like this:
select
price,
item_id,
sum(price),
count(item_id)
from transactions
group by
(price <= 20),
(price between 21 and 30),
(price between 31 and 40),
(price between 41 and 50),
(price > 50)
I have never done a group like this before when I wrote it I was just guessing to see if the query was even valid, and it was. But my question is, is it really getting me what I want?
I want all transactions grouped by:
Items that cost less than or equal to $20
Items that cost between $21 and $30
Items that cost between $31 and $40
Items that cost between $41 and $50
Items that cost more than $50
So, is that query doing what I am asking?
The way to do this in standard SQL (and MySQL) is to use the case statement. Also, I put the definition in a subquery like this:
select pricegrp, sum(price), count(item_id)
from (select t.*,
(case when price <= 20 then '00-20'
when price between 21 and 30 then '21-30'
when price between 31 and 40 then '31-40'
when price between 41 and 50 then '41-50'
when price > 50 then '50+'
end) as pricegrp
from transactions t
) t
group by pricegrp
Also, do you want to group by item_id as well? Or are you just trying to return one arbitrary item? Based on what you want, I'm removing the item_id from the select clause. It doesn't seem necessary.
Your query actually does work in MySQL, in the sense that it runs. It is going to produce one row for each group that you want, so in that sense it "works". However, within each group, it is going to choose an arbitrary price and item_id. These are not explicitly mentioned in the group by clause, so you are using a MySQL (mis)feature called Hidden Columns. Different runs of the query or slight changes to the data or slight changes to the query can change the values of price and item_id returned for each group.
I strongly suggest that you actually name the group. This makes the query and the output much clearer.
Also, I recommend that you get in the habit of putting all columns in the select in the group by clause. There are a few cases where hidden columns are actually useful, but I think, in general, you should depend on them sparingly.
If the price is not stored as an integer, then correct logic is:
select pricegrp, sum(price), count(item_id)
from (select t.*,
(case when price <= 20 then '00-20'
when price <= 30 then '21-30'
when price <= 40 then '31-40'
when price <= 50 then '41-50'
when price > 50 then '50+'
end) as pricegrp
from transactions t
) t
group by pricegrp
SELECT
price,
item_id,
sum(price),
count(item_id),
IF(price<=20,0,IF(price<=30,1,IF(price<=40,2,IF(price<=50,3,4)))) AS pricegroup
FROM transactions
GROUP BY pricegroup
or even
SELECT
price,
item_id,
sum(price),
count(item_id)
FROM transactions
GROUP BY
IF(price<=20,0,IF(price<=30,1,IF(price<=40,2,IF(price<=50,3,4))))
SELECT price,
item_id,
SUM(CASE WHEN price <= 20 THEN price ELSE 0 END) `(price <= 20) SUM`,
SUM(CASE WHEN price <= 20 THEN 1 ELSE 0 END) `(price <= 20) COUNT`,
SUM(CASE WHEN price between 21 and 30 THEN price ELSE 0 END) `(price <= 20) SUM`,
SUM(CASE WHEN price between 21 and 30 THEN 1 ELSE 0 END) `(price <= 20) COUNT`,
SUM(CASE WHEN price between 31 and 40 THEN price ELSE 0 END) `price between 31 and 40 SUM`,
SUM(CASE WHEN price between 31 and 40 THEN 1 ELSE 0 END) `price between 31 and 40 COUNT`,
SUM(CASE WHEN price between 41 and 50 THEN price ELSE 0 END) `price between 41 and 50 SUM`,
SUM(CASE WHEN price between 41 and 50 THEN 1 ELSE 0 END) `price between 41 and 50 COUNT`,
SUM(CASE WHEN price > 50 THEN price ELSE 0 END) `price > 50 SUM`,
SUM(CASE WHEN price > 50 THEN 1 ELSE 0 END) `price > 50 COUNT`
FROM transactions
GROUP BY price, item_id