How to combine this MySQL queries? - mysql

I have 2 tables like this:
table:prices
id, project_id, real_min_price, real_max_price
1 | 100100 | 500 | 2000
2 | 100100 | 900 | 3000
3 | 100100 | 2500 | 3200
4 | 100100 | 320 | 3900
table:gifts
id, project_id, min_price, max_price, gift
1 | 100100 | 0 | 1000 | 10
2 | 100100 | 1001 | 2000 | 20
3 | 100100 | 2001 | 3000 | 30
4 | 100100 | 3001 | 4000 | 40
5 | 100100 | 4001 | 5000 | 50
6 | 100100 | 5001 | 6000 | 60
$ID = 100100;
// find highest price
SELECT MAX(real_max_price) FROM `prices` WHERE project_id='$ID';
$MAX_PROJECT_PRICE = $dbo->getOne();
-- returns 3900
// find the limit row which between min-max columns of this value
SELECT gift FROM `gifts` WHERE project_id='$ID'
AND max_price>='$MAX_PROJECT_PRICE' ORDER BY max_price ASC LIMIT 1;
$MAX_GIFT = $dbo->getOne();
-- founded 4th row of the gifts table and returns 40
// remove other gift rows higher then MAX_GIFT value
DELETE FROM `gifts` WHERE project_id='$ID' AND gift>'$MAX_GIFT';
-- deleted 5th and 6th rows.
in this scenario
it will find max price as "3900" so, 5th and 6th row of gifts table will be removed.
but this way is really bad, it should be done in one query but how?

Okay you can except delete query, at least maybe we can combine first
2 queries to find MAX_GIFT value.
To combine the first two queries you can do.
Query
SELECT
gift
FROM
gifts
WHERE
project_id = 100100
AND max_price >= (SELECT MAX(real_max_price)
FROM prices
WHERE project_id = 100100;)
ORDER BY
max_price ASC
LIMIT 1;
And using all three queries into one.
Query
DELETE FROM
gifts
WHERE
project_id = 100100
AND
gift > (SELECT
gift
FROM
gifts
WHERE
project_id = 100100
AND
max_price >= (SELECT
MAX(real_max_price)
FROM
prices
WHERE
project_id = 100100;
)
ORDER BY
max_price ASC
LIMIT 1
)

Here is a solution to run the delete command based on your queries conditions:
delete from gifts where gift in
(
select gift from
(
select gift, #row:=#row+1 as rw
from gifts, (select #row:=0) a
where (select max(real_max_price)
from prices
where project_id=100100 limit 1) < max_price
and project_id = 100100
order by gift
) sub
where rw>1
);
Sample data and test.
create table prices (
id integer,
project_id integer,
real_min_price integer,
real_max_price integer
);
create table gifts (
id integer,
project_id integer,
min_price integer,
max_price integer,
gift integer
);
insert into prices values
(1,100100,500,2000),
(2,100100,900,3000),
(3,100100,2500,3200),
(4,100100,320,3900);
insert into gifts values
(1 , 100100 , 0 , 1000 , 10),
(2 , 100100 , 1001 , 2000 , 20),
(3 , 100100 , 2001 , 3000 , 30),
(4 , 100100 , 3001 , 4000 , 40),
(5 , 100100 , 4001 , 5000 , 50),
(6 , 100100 , 5001 , 6000 , 60);
Running the query I provided you will get on your gifts table.
1 100100 0 1000 10
2 100100 1001 2000 20
3 100100 2001 3000 30
4 100100 3001 4000 40

Related

Running total in two tables

Consider two tables: invoices and payments. The invoices table contains records of invoices raised, and the payments table contains records of payments received.
invoices
id
date
cname
amount
1
2021-12-12
cname1
10000
2
2021-12-13
cname2
5000
3
2022-01-15
cname1
7000
4
2022-01-16
cname2
1000
payments
id
date
cname
amount
1
2022-01-05
cname1
5000
2
2022-01-07
cname2
5000
3
2022-02-05
cname1
10000
4
2022-02-06
cname2
1000
CALCULATE RUNNING BALANCE
Q) Extend the SQL query to do invoice / payment matching as follows (as of 28/2/2022)
matching
date
document_id
cname
amount
due
2021-12-12 00:00:00
1
cname1
10000
10000
2022-01-05 00:00:00
1
cname1
-5000
5000
2022-01-15 00:00:00
3
cname1
7000
12000
2022-02-05 00:00:00
3
cname1
-10000
2000
2021-12-13 00:00:00
2
cname2
5000
5000
2022-01-07 00:00:00
2
cname2
-5000
0
2022-01-16 00:00:00
4
cname2
1000
1000
2022-02-06 00:00:00
4
cname2
-1000
0
You can union both tables considering the second one with negative amount, and then a simple running total will produce the result you want. For example:
select
date,
id as document_id,
cname,
amount,
sum(amount) over(partition by id order by date) as due
from (
select * from invoices
union all select id, date cname, -amount from payments
) x
order by cname, date
SELECT `date`,
documentId,
cname,amount,
due FROM (SELECT `date`,
documentId,
cname,
amount,
(CASE WHEN #running_customer='' THEN #running_balance:=amount
WHEN #running_customer=cname THEN #running_balance:=#running_balance+amount ELSE #running_balance:=amount END) due,
#running_customer:=cname
FROM (SELECT `date`, id AS documentId,cname, amount FROM `invoices`i
UNION ALL
SELECT `date`, id AS documentId,cname, amount*-1 AS actionType FROM `payments` p) final
JOIN (SELECT #running_customer:='') rc
JOIN (SELECT #running_balance:=0) rb
ORDER BY cname, `date`) finalResult
You need to be using assignment operator for these kind of problems.

Conditional Window Functions

I have a sales table that looks like this:
store_id cust_id txn_id txn_date amt industry
200 1 1 20180101 21.01 1000
200 2 2 20200102 20.01 1000
200 2 3 20200103 19 1000
200 3 4 20180103 19 1000
200 4 5 20200103 21.01 1000
300 2 6 20200104 1.39 2000
300 1 7 20200105 12.24 2000
300 1 8 20200105 25.02 2000
400 2 9 20180106 103.1 1000
400 2 10 20200107 21.3 1000
Here's the code to generate this sample table:
CREATE TABLE sales(
store_id INT,
cust_id INT,
txn_id INT,
txn_date bigint,
amt float,
industry INT);
INSERT INTO sales VALUES(200,1,1,20180101,21.01,1000);
INSERT INTO sales VALUES(200,2,2,20200102,20.01,1000);
INSERT INTO sales VALUES(200,2,3,20200103,19.00,1000);
INSERT INTO sales VALUES(200,3,4,20180103,19.00,1000);
INSERT INTO sales VALUES(200,4,5,20200103,21.01,1000);
INSERT INTO sales VALUES(300,2,6,20200104,1.39,2000);
INSERT INTO sales VALUES(300,1,7,20200105,12.24,2000);
INSERT INTO sales VALUES(300,1,8,20200105,25.02,2000);
INSERT INTO sales VALUES(400,2,9,20180106,103.1,1000);
INSERT INTO sales VALUES(400,2,10,20200107,21.3,1000);
What I would like to do is create a new table, results that answers the question: what percentage of my VIP customers have, since January 3rd 2020, shopped i) at my store only; ii) at my store and at other stores in the same industry; iii) at only other stores in the same industry? Define a VIP customer to be someone who has shopped at a given store at least once since 2019.
Here's the target output table:
store industry pct_my_store_only pct_both pct_other_stores_only
200 1000 0.5 0.5 0.0
300 2000 0.5 0.5 0.0
400 1000 0.0 1.0 0.0
I'm trying to use window functions to accomplish this. Here's what I have so far:
CREATE TABLE results as
SELECT s.store_id, s.industry,
COUNT(DISTINCT (CASE WHEN s.txn_date>=20200103 THEN s.cust_id END)) * 1.0 / sum(count(DISTINCT (CASE WHEN s.txn_date>=20200103 THEN s.cust_id END))) OVER (PARTITION BY s.industry) AS pct_my_store_only
...AS pct_both
...AS pct_other_stores_only
FROM sales s
WHERE sales.txn_date>=20190101
GROUP BY s.store_id, s.industry;
The above does not seem to be correct; how can I correct this?
Join the distinct store_ids and industries to the concatenated distinct store_ids and industries for each customer and then use window function avg() with the function find_in_set() to determine if a customer how many customer have shopped or not from each store:
with
stores as (
select distinct store_id, industry
from sales
where txn_date >= 20190103
),
customers as (
select cust_id,
group_concat(distinct store_id) stores,
group_concat(distinct industry) industries
from sales
where txn_date >= 20190103
group by cust_id
),
cte as (
select *,
avg(concat(s.store_id) = concat(c.stores)) over (partition by s.store_id, s.industry) pct_my_store_only,
avg(find_in_set(s.store_id, c.stores) = 0) over (partition by s.industry) pct_other_stores_only
from stores s inner join customers c
on find_in_set(s.industry, c.industries) and find_in_set(s.store_id, c.stores)
)
select distinct store_id, industry,
pct_my_store_only,
1 - pct_my_store_only - pct_other_stores_only pct_both,
pct_other_stores_only
from cte
order by store_id, industry
See the demo.
Results:
> store_id | industry | pct_my_store_only | pct_both | pct_other_stores_only
> -------: | -------: | ----------------: | -------: | --------------------:
> 200 | 1000 | 0.5000 | 0.5000 | 0.0000
> 300 | 2000 | 0.5000 | 0.5000 | 0.0000
> 400 | 1000 | 0.0000 | 1.0000 | 0.0000

Single query to retrieve multiple values from multiple tables

Expenses table
1/1/2016 exp1 2000
13/1/2016 exp11 2500
1/2/2016 exp2 1500
1/3/2016 exp3 1000
10/3/2016 exp1 2000
Income table
1/1/2016 income1 2500
1/2/2016 income2 3500
1/3/2016 income3 1500
10/3/2016 income3 1000
1/4/2016 income4 5000
From single query what I need is group by month, this is what I need
Expenses Incomes Month
4500 2500 Jan
1500 3500 Feb
3000 2500 Mar
0 5000 April
I need the above query to show the data in Google graph
Terrible data structure and format, but not impossible:
SELECT
IFNULL(exp.Expenses,0) Expenses,
IFNULL(inc.Incomes,0) Incomes,
inc.`monthname` `Month`
FROM
(
SELECT
SUM(i.amount) Incomes,
MONTHNAME(STR_TO_DATE(i.`date`, '%d/%m/%Y')) `monthname`,
MONTH(STR_TO_DATE(i.`date`, '%d/%m/%Y')) `month`
FROM
incomes i
GROUP BY
MONTHNAME(STR_TO_DATE(i.`date`, '%d/%m/%Y')),
MONTH(STR_TO_DATE(i.`date`, '%d/%m/%Y'))
) inc
LEFT JOIN
(
SELECT
SUM(e.amount) Expenses,
MONTHNAME(STR_TO_DATE(e.`date`, '%d/%m/%Y')) `monthname`,
MONTH(STR_TO_DATE(e.`date`, '%d/%m/%Y')) `month`
FROM
expenses e
GROUP BY
MONTHNAME(STR_TO_DATE(e.`date`, '%d/%m/%Y')),
MONTH(STR_TO_DATE(e.`date`, '%d/%m/%Y'))
) exp
ON exp.`month` = inc.`month`
ORDER BY
inc.`month`
Output of this simplicity:
+----------+---------+----------+
| Expenses | Incomes | Month |
+----------+---------+----------+
| 4500 | 2500 | January |
| 1500 | 3500 | February |
| 3000 | 2500 | March |
| 0 | 5000 | April |
+----------+---------+----------+
4 rows in set
Anyway better thing seriously how to improve and normalize your data.
In my solution, I give the number of the month rather than text. I'll leave it to you to convert it to text (using a CASE expression, for example) if you wish:
SELECT
sum(expense) AS total_expense, sum(income) AS total_income, trans_month
FROM (
SELECT
month(trans_date) AS trans_month,
0 AS income,
sum(amount) AS expense
FROM expense
GROUP BY month(trans_date)
UNION ALL
SELECT
month(trans_date) AS trans_month,
sum(amount) AS income,
0 AS expense
FROM income
GROUP BY month(trans_date)
) AS a
GROUP BY trans_month;

Mysql sum of field values - count , with group by , order by , limit function

I have Table user_views
My Tablet Sample data
Id | product_id | category | user_id | sitename| price | click_count | created_date
1 10 watch 102 ebay 820 1 2014-08-18 13:56:05
2 10 watch 102 amazon 750 1 2014-08-19 13:56:05
3 10 watch 102 amazon 740 1 2014-08-19 18:00:05
4 10 watch 102 ebay 940 1 2014-08-25 08:00:00
5 10 watch 102 amazon 640 5 2014-08-25 08:10:10
6 10 watch 102 ebay 580 3 2014-09-25 18:10:10
7 10 watch 102 amazon 980 5 2014-10-05 12:20:40
I want the total count of the user visited this product
My Query
"select Id , proudct_id , category , user_id , count(click_count) as cnt from user_view where user_id =102 group by product_id order by rand() limit 0,10"
But the output is showing only one count
OUTPUT
Id | product_id | category | user_id | cnt
1 10 watch 102 1
EXPECTED OUTPUT IS
Id | product_id | category | user_id | cnt
1 10 watch 102 17
You should be using sum aggregate function instead of count which will count number of rows. So your query should be something like:
select Id , product_id , category , user_id , SUM(click_count) as sum ...
Try this:
select Id , proudct_id , category , user_id , SUM(click_count) as cnt
from user_view
where user_id =102
order by rand()
limit 0,10
group by product_id
COUNT() always returns the number of items, not the sum of values. So use SUM().
"select Id , proudct_id , category , user_id , count(click_count) as cnt from user_view where user_id =102 group by user_id, product_id order by rand() limit 0,10"

Getting Max date from multiple table after INNER JOIN

I have two following tables
table 1)
ID | HOTEL ID | NAME
1 100 xyz
2 101 pqr
3 102 abc
table 2)
ID | BOOKING ID | DEPARTURE DATE | AMOUNT
1 1 2013-04-12 100
2 1 2013-04-14 120
3 1 2013-04-9 90
4 2 2013-04-14 100
5 2 2013-04-18 150
6 3 2013-04-12 100
I want to get reault in mysql such that it take the row from table two with MAX DEPARTURE DATE.
ID | BOOKING ID | DEPARTURE DATE | AMOUNT
2 1 2013-04-14 120
5 2 2013-04-18 150
6 3 2013-04-12 100
SELECT b.ID,
b.BookingID,
a.Name,
b.departureDate,
b.Amount
FROM Table1 a
INNER JOIN Table2 b
ON a.ID = b.BookingID
INNER JOIN
(
SELECT BookingID, MAX(DepartureDate) Max_Date
FROM Table2
GROUP BY BookingID
) c ON b.BookingID = c.BookingID AND
b.DepartureDate = c.Max_date
SQLFiddle Demo
Well,
SELECT * FROM `table2` ORDER BY `DEPARTURE_DATE` DESC LIMIT 0,1
should help