SQL for my requirement? - mysql

I need a SQL query to list all the customers who have joined in last 6 months.
This is my SQL for that.
select
c.company_name,
c.phone1,
c.sprovince,
c.scountry,
sum(order_total_amount) as amt_sold,
max(o.order_date) as last_order_date,
customersince
from
tbl_company c
join
tbl_order o
on c.companyid = o.company_id
where
c.companytype like 'Customer'
and
(PERIOD_DIFF(c.customersince,curdate())<6)
group by company_name
order by amt_sold desc
Now I need one more or condition so that if there is null in customersince column then I should check the first order of the customer. If it is in last 6 months I should display that user also.
order_date is available in tbl_order table. The first order of the customer is min(order_date) group by customer_id
How can I do this?

SELECT c.company_name,
c.phone1,
c.sprovince,
c.scountry,
SUM(order_total_amount) as amt_sold,
MAX(o.order_date) as last_order_date,
COALESCE(customersince, MIN(o.order_date)) AS customersince
FROM tbl_order o
JOIN tbl_company c
ON c.companyid = o.company_id
WHERE c.companytype like 'Customer'
AND (c.customersince >= NOW() - INTERVAL 6 MONTH OR c.customersince IS NULL)
GROUP BY
companyid
HAVING customersince >= NOW() - INTERVAL 6 MONTH
ORDER BY
amt_sold DESC
Note the double condition on customersince: one in the WHERE clause, another one in the HAVING clause.
If you have an index on tbl_customer (customersince), this index will be used to filter the appropriate records early (and fine-filter them later).

PERIOD_DIFF(ifnull(c.customersince, `first_order`),curdate())<6)
you should include your table schema, your first order is refer to which table?

I don't have mysql to test, but this where clause might work for you:
where c.companytype like 'customer'
and (
(PERIOD_DIFF(c.customersince, curdate())<6)
OR
(c.customersince is null AND (PERIOD_DIFF(o.order_date, curdate())<6)
)
Also, you will have to add things you don't have in an aggregate function (max, sum, etc) to your group by clause.

Related

MySQL Error: 1111 (Invalid use of group function) [duplicate]

This question already has an answer here:
ERROR 1111 (HY000): Invalid use of group function
(1 answer)
Closed 1 year ago.
I'm not yet good at MySQL. Please check my sql below and help me understand where I went wrong with it. All I need is just one record for the order.id and the returned record must be the one whose shipped date is the latest.
Database error: Invalid SQL: SELECT orders.id, orders.customer_fk FROM orders INNER JOIN order_details ON order_details.order_fk=orders.id WHERE orders.payment_method IN ('AS','AC') AND ((orders.order_status='SHP' AND order_details.item_status='SHP' AND MAX(order_details.shipped_date) <= '2021-08-07') OR (orders.order_status='CAN' AND orders.order_date <= '2021-08-07 09:56:18')) AND orders.pii_status <> '1'GROUP BY orders.id
MySQL Error: 1111 (Invalid use of group function)
Instead of using MAX alone try to use a subselect
If you don't want the mad for every order.id then you need to add a inner join
SELECT
orders.id, orders.customer_fk
FROM
orders
INNER JOIN
order_details ON order_details.order_fk = orders.id
WHERE
orders.payment_method IN ('AS' , 'AC')
AND ((orders.order_status = 'SHP'
AND order_details.item_status = 'SHP'
AND (SELECT MAX(shipped_date) FROM order_details WHERE order_fk = orders.id) <= '2021-08-07')
OR (orders.order_status = 'CAN'
AND orders.order_date <= '2021-08-07 09:56:18'))
AND orders.pii_status <> '1'
GROUP BY orders.id
To explain it somewhat further
SELECT MAX(shipped_date) FROM order_details WHERE order_fk = orders.id) <= '2021-08-07'
Return true or false for every Order.id as it checks for every row in the outer select what the maximum date is and then checks it against the date.
After selecting all rows you GROUP BY(which i still don't get as you have no aggregation function it) comes for every order.id.
Maybe you should try a DISTINCT
You select both orders.id and orders.customer_fk, but you group by orders.id only. When using group by in SQL, all other columns not present in the group by clause must be aggregate functions, since for example in this current case you group the results by the order id, meaning there can be only one row per unique order id among the results.
And something has to happen with the list of values of the other column that all belong to this one grouped order id, this is where the aggregations come in. If it is a number you can calculate the MIN/MAX/AVG etc. of these, but the simplest aggregate is to just count the matching results.
So if you wanted your query to return the number of order.customer_fk for each unique order.id, just add SELECT orders.id, COUNT(orders.customer_fk).
Otherwise, if you didn't intend to group your results, you can remove the GROUP BY clause, or replace it with an ORDER BY.
If you want to filter using aggregation functions use having. However, I'm guessing that you just want to filter by the date:
SELECT o.id, o.customer_fk
FROM orders o INNER JOIN
order_details od
ON od.order_fk= o.id
WHERE o.payment_method IN ('AS','AC') AND
((o.order_status = 'SHP' AND od.item_status='SHP' AND od.shipped_date <= '2021-08-07') OR
(o.order_status = 'CAN' AND o.order_date <= '2021-08-07 09:56:18')
) AND
o.pii_status <> '1'
GROUP BY o.id

MYSQL select max date from joined tables

I have 2 tables which I want to join and retrieve some specific data. These are my tables.
tbl_user (reg_id, l_name, f_name, status)
tbl_payments (pay_id, reg_id, mem_plan, from_date, to_date, bill_no, payed_date)
What I need to do is select and view the users who have due payments. To do that I want to get the user details where "status=0" from tbl_user and join the 2 tables together and the conditions are to_date< current date, difference between [current date and the to_date] < 31 and filter by the Max value of to_date.
What I did so far gives me a result according to above mentioned conditions except it dosen't filter by the MAX(to_date). This is my query.
SELECT
A.reg_id,
A.f_name,
A.l_name,
B.mem_plan,
B.from_date,
Max(B.to_date) AS to_date,
B.bill_no,
B.payed_date
FROM
tbl_user A,
tbl_payments B
WHERE
A.status = 0
AND A.reg_id = B.reg_id
AND Date(Now()) >= Date(B.to_date)
AND Datediff(Date(Now()), Date(b.to_date)) < 31
GROUP BY
a.reg_id, b.mem_plan, b.from_date, b.bill_no, b.payed_date;
I'm not very familiar with MYSQL, So please someone tell me what I did wrong or if this query is not up to the standard.
Here are some sample data to work on.
tbl_user ( [M1111,Jon, Doe,0], [M1112,Jane,Doe,1],[M1113,Jony,Doe,0] )
tbl_payment ( [1,M1111,Monthly,2018-05-14,2018-06-14,b123,2018-05-14],[2,M1112,3Months,2018-02-03,2018-05-03,b112,2018-02-03],[3,M1113,Monthly,2018-06-14,2018-07-14,b158,2018-06-14],[4,M1111,Monthly,2018-06-15,2018-07-15,b345,2018-06-15],[5,M1113,Monthly,2018-06-06,2018-07-06,b158,2018-06-06],[6,M1111,Monthly,2018-07-05,2018-08-05,b345,2018-07-05] )
Assuming current date is 2018-07-17, The expecting result should be this
[M1111,Jon,Doe,Monthly,2018-06-15,2018-07-15,b345,2018-06-15],[M1113,Jony,Doe,Monthly,2018-06-14,2018-07-14,b158,2018-06-14]
Instead of that, my query gives me this.
[M1111,Jon,Doe,Monthly,2018-06-15,2018-07-15,b345,2018-06-15],[M1113,Jony,Doe,Monthly,2018-06-06,2018-07-06,b158,2018-06-06],
[M1113,Jony,Doe,Monthly,2018-06-14,2018-07-14,b158,2018-06-14]
I wrote another query which gives me the result set exactly as i want. But I'm not sure whether it's up to the standards. If someone can simplify this or make it better, appreciate very much.
SELECT A.reg_id,A.f_name,A.l_name,D.mem_plan,D.from_date,D.to_date,D.bill_no,D.payed_date
FROM tbl_user A
JOIN (SELECT B.reg_id,B.mem_plan,B.from_date,B.to_date,B.bill_no,B.payed_date
FROM tbl_payments B
JOIN (
SELECT reg_id, MAX(to_date) as to_date
FROM tbl_payments
WHERE DATE(NOW()) >= DATE(to_date) AND DATEDIFF(DATE(NOW()), DATE(to_date))<31
GROUP BY reg_id) C
ON B.reg_id = C.reg_id AND B.to_date= C.to_date) D
ON A.reg_id = D.reg_id
WHERE A.status=0;
I believe having won't work here and that your second query is about as good as it gets. I've condensed it a little here:
SELECT A.reg_id,f_name,l_name,mem_plan,from_date,to_date,bill_no,payed_date
FROM #tbl_user A
JOIN #tbl_payments B ON A.reg_id = b.reg_id
JOIN (
SELECT reg_id, MAX(to_date) as max_to_date
FROM #tbl_payments
WHERE DATE(NOW()) >= DATE(to_date) AND DATEDIFF(DATE(NOW()), DATE(to_date))<31
GROUP BY reg_id
) C ON B.reg_id = C.reg_id AND B.to_date= C.max_to_date
WHERE A.status=0;

how to mysql group by date multiple left join

I have the following schema:
http://sqlfiddle.com/#!9/bd3a4/1
I would like to
group by date() and add where user_id = ?..
per day and count the results per day.
required result Day|TotalRequests|TotalOrders
Since you could have an order on Day 1, and a request on Day 8, you may have entries on one side but not the other. To qualifify your needs, I would do a UNION of all orders and requests individually by date. Then roll those values up. The inner Pre-Aggregate result query is where the WHERE clause per user would be applied. The pre-aggregate query also has a recSource column to indicate where the record originated from as 'O' from orders and 'R' from requests, so the roll-up knows which column to store the total count respectively.
select
preAgg.recDate,
SUM( case when preAgg.recSource = 'O' then preAgg.recCount else 0 end ) as OrderCount,
SUM( case when preAgg.recSource = 'R' then preAgg.recCount else 0 end ) as RequestCount
from
( select
date(o.created_at) recDate,
'O' as recSource,
count(*) as recCount
from
orders o
where
o.user_id = 3
group by
date(o.created_at)
UNION ALL
select
date(r.created_at) recDate,
'R' as recSource,
count(*) as recCount
from
requests r
where
r.user_id = 3
group by
date(r.created_at) ) preAgg
group by
preAgg.recDate
order by
preAgg.recDate
For query optimization, I would ensure your order and request table both have have an index on ( user_id, created_at ).
SQL Fiddle result
You can use the following query:
SELECT
DATE(o.created_at) AS Day
,COUNT(r.id) AS TotalRequests
,COUNT(o.id) AS TotalOrders
FROM orders o
LEFT JOIN
requests r ON
r.id = o.request_id
WHERE o.user_id = 3
GROUP BY DATE(r.created_at), DATE(o.created_at),o.user_id

MySQL GROUP BY grouping by lowest field value

I'm trying to fetch the lowest price per day per hotel, I get multiple results.
I first try to fetch the lowest amount with the MIN() function, then inner join.
When i later try to group by outside the subquery, it just groups by the lowest id.
The SQL itself:
SELECT mt.id, mt.amount, mt.fk_hotel, mt.start_date
FROM price mt
INNER JOIN
(
SELECT price.id, MIN(price.amount) minAmount
FROM price
WHERE 1=1 AND price.start_date >= '2014-10-08' AND price.start_date <= '2014-10-10' AND price.active = 1 AND price.max_people = 2
GROUP BY id
) t
ON mt.id = t.id AND mt.amount = t.minAmount
ORDER BY mt.fk_hotel, mt.amount;
And the results looks like this:
http://jsfiddle.net/63mg3b2j/
I want to group by the start date and fk_hotel so that it groups by the lowest amount value, can anybody help me? Am I being clear?
Edit: I also need a field fk_room from the corresponding row, so i can inner join
Try this:
SELECT MIN(mt.amount) AS min_amount, mt.fk_hotel, mt.start_date
FROM price mt
WHERE
mt.active = 1 AND
mt.max_people = 2 AND
mt.start_date >= '2014-10-08' AND mt.start_date <= '2014-10-10'
GROUP BY mt.fk_hotel, mt.start_date
ORDER BY mt.fk_hotel, min_amount;
Well first of all get a table with minimum value in top row using ORDER BY and then GROUP BY for your required result
SELECT mt.id, mt.amount, mt.fk_hotel, mt.start_date
FROM
(SELECT id, amount, fk_hotel, start_date
FROM price
WHERE start_date >= '2014-10-08' AND start_date <= '2014-10-10'
AND active = 1 AND max_people = 2
ORDER BY amount DESC) AS mt
GROUP BY mt.id
Well I had to still go with a subquery, cause i needed some additional foreign key fields from the corresponding row to inner join some other stuff. It isn't a great solution, cause it fetches too much stuff, the rest is filtered out programmatically.
The most annoying thing here, when I try to use MIN() or MAX() function and get the appropriate fields to that row, it fetches the first results from the DB, which are incorrect and so i have to use a subquery to inner join to get the other fields, I can use grouping, but I had too many fields to group. Maybe I'm missing something. The amount of data doesn't grow in time, so I guess it works for me. So this is the final SQL i came up with, for future reference..
SELECT mt.*, roomtype.name roomname, hotel.name hotelname
FROM booking.price mt
INNER JOIN roomtype ON roomtype.id = mt.fk_roomtype
INNER JOIN hotel ON hotel.id = mt.fk_hotel
INNER JOIN(
SELECT price.id, MIN(price.amount) minAmount
FROM booking.price WHERE 1=1 AND price.start_date >= '2014-10-22' AND price.start_date <= '2014-10-31' AND price.max_people = 2 AND price.active = 1
GROUP BY id
) t
ON mt.id = t.id AND mt.amount = t.minAmount
ORDER BY mt.start_date, mt.amount

Mysql count() fetches wrong result

I am trying to find the number of records for current week.
My current query is:
SELECT Week(Str_to_date(products_options_values, '%m-%d-%Y'), 1) AS order_week,
Year(Str_to_date(products_options_values, '%m-%d-%Y')) AS order_year,
order_active,
Count(op.sub_order_id) AS deliveries
FROM orders_products_attributes opa
LEFT JOIN orders_products op
ON ( opa.orders_products_id = op.orders_products_id )
GROUP BY order_week,
order_year
HAVING order_week = '31'
AND order_year >= '2013'
AND order_active = 0
ORDER BY order_week
It fetches deliveries AS 2 where as there are actually 4 records, and if I run the same query after removing COUNT and GROUP BY, it correctly shows all 4 rows. The same problem happens on other weeks too, for example week 34 has 3 records, but the above query fetches it as 4 instead. Moreover, another weird thing is, in the GROUP BY clause, if I remove either one of order_week or order_year the query returns an empty result set.
Any idea what I am doing wrong?
Try to move all HAVING conditions into WHERE. Also Count(id) - counts UNIQUE values of ID not all. If you need all records count just use COUNT(*)
SELECT Week(Str_to_date(products_options_values, '%m-%d-%Y'), 1) AS order_week,
Year(Str_to_date(products_options_values, '%m-%d-%Y')) AS order_year,
order_active,
Count(op.sub_order_id) AS deliveries
FROM orders_products_attributes opa
LEFT JOIN orders_products op
ON ( opa.orders_products_id = op.orders_products_id )
WHERE order_week = '31'
AND order_year >= '2013'
AND order_active = 0
GROUP BY order_week,
order_year
ORDER BY order_week