Mysql count() fetches wrong result - mysql

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

Related

Select most recent record grouped by 3 columns

I am trying to return the price of the most recent record grouped by ItemNum and FeeSched, Customer can be eliminated. I am having trouble understanding how I can do that reasonably.
The issue is that I am joining about 5 tables containing hundreds of thousands of rows to end up with this result set. The initial query takes about a minute to run, and there has been some trouble with timeout errors in the past. Since this will run on a client's workstation, it may run even slower, and I have no access to modify server settings to increase memory / timeouts.
Here is my data:
Customer Price ItemNum FeeSched Date
5 70.75 01202 12 12-06-2017
5 70.80 01202 12 06-07-2016
5 70.80 01202 12 07-21-2017
5 70.80 01202 12 10-26-2016
5 82.63 02144 61 12-06-2017
5 84.46 02144 61 06-07-2016
5 84.46 02144 61 07-21-2017
5 84.46 02144 61 10-26-2016
I don't have access to create temporary tables, or views and there is no such thing as a #variable in C-tree, but in most ways it acts like MySql. I wanted to use something like GROUP BY ItemNum, FeeSched and select MAX(Date). The issue is that unless I put Price into the GROUP BY I get an error.
I could run the query again only selecting ItemNum, FeeSched, Date and then doing an INNER JOIN, but with the query taking a minute to run each time, it seems there is a better way that maybe I don't know.
Here is my query I am running, it isn't really that complicated of a query other than the amount of data it is processing. Final results are about 50,000 rows. I can't share much about the database structure as it is covered under an NDA.
SELECT DISTINCT
CustomerNum,
paid as Price,
ItemNum,
n.pdate as newest
from admin.fullproclog as f
INNER JOIN (
SELECT
id,
itemId,
MAX(TO_CHAR(pdate, 'MM-DD-YYYY')) as pdate
from admin.fullproclog
WHERE pdate > timestampadd(sql_tsi_year, -3, NOW())
group by id, itemId
) as n ON n.id = f.id AND n.itemId = f.itemId AND n.pdate = f.pdate
LEFT join (SELECT itemId AS linkid, ItemNum FROM admin.itemlist) AS codes ON codes.linkid = f.itemId AND ItemNum >0
INNER join (SELECT DISTINCT parent_id,
MAX(ins1.feesched) as CustomerNum
FROM admin.customers AS p
left join admin.feeschedule AS ins1
ON ins1.feescheduleid = p.primfeescheduleid
left join admin.group AS c1
ON c1.insid = ins1.feesched
WHERE status =1
GROUP BY parent_id)
AS ip ON ip.parent_id = f.parent_id
WHERE CustomerNum >0 AND ItemNum >0
UNION ALL
SELECT DISTINCT
CustomerNum,
secpaid as Price,
ItemNum,
n.pdate as newest
from admin.fullproclog as f
INNER JOIN (
SELECT
id,
itemId,
MAX(TO_CHAR(pdate, 'MM-DD-YYYY')) as pdate
from admin.fullproclog
WHERE pdate > timestampadd(sql_tsi_year, -3, NOW())
group by id, itemId
) as n ON n.id = f.id AND n.itemId = f.itemId AND n.pdate = f.pdate
LEFT join (SELECT itemId AS linkid, ItemNum FROM admin.itemlist) AS codes ON codes.linkid = f.itemId AND ItemNum >0
INNER join (SELECT DISTINCT parent_id,
MAX(ins1.feesched) as CustomerNum
FROM admin.customers AS p
left join admin.feeschedule AS ins1
ON ins1.feescheduleid = p.secfeescheduleid
left join admin.group AS c1
ON c1.insid = ins1.feesched
WHERE status =1
GROUP BY parent_id)
AS ip ON ip.parent_id = f.parent_id
WHERE CustomerNum >0 AND ItemNum >0
I feel it quite simple when I'd read the first three paragraphs, but I get a little confused when I've read the whole question.
Whatever you have done to get the data posted above, once you've got the data like that it's easy to retrive "the most recent record grouped by ItemNum and FeeSched".
How to:
Firstly, sort the whole result set by Date DESC.
Secondly, select fields you need from the sorted result set and group by ItemNum, FeeSched without any aggregation methods.
So, the query might be something like this:
SELECT t.Price, t.ItemNum, t.FeeSched, t.Date
FROM (SELECT * FROM table ORDER BY Date DESC) AS t
GROUP BY t.ItemNum, t.FeeSched;
How it works:
When your data is grouped and you select rows without aggregation methods, it will only return you the first row of each group. As you have sorted all rows before grouping, so the first row would exactly be "the most recent record".
Contact me if you got any problems or errors with this approach.
You can also try like this:
Select Price, ItemNum, FeeSched, Date from table where Date IN (Select MAX(Date) from table group by ItemNum, FeeSched,Customer);
Internal sql query return maximum date group by ItemNum and FeeSched and IN statement fetch only the records with maximum date.

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

Grouping MySQL query by date range and productId

So I am trying to alter my sql code (see below for screenshot of current results + sql) to group the data by the month AND sum up all the paymentSplitAmounts. Each row should be a unique productId
So the end result would be something like
productID total month
1 500 11-2011
2 650 11-2011
3 250 11-2011
1 100 10-2011
2 150 10-2011
3 750 10-2011
I can't seem to get the syntax right. Where am I going wrong?
http://imgur.com/UC5Si
select
cpd.paymentId, cpd.paymentId, cpd.productId, cpd.paymentSplitAmount, cp.campaignId, cp.paymentDate
from campaign_payment_detail cpd
inner join
campaign_payment cp on cp.paymentId = cpd.paymentId
inner join product on cpd.productId = product.productId
where
1=1
and cp.campaignId = 2413
Looks like you want to group then sort your results:
SELECT cpd.productId, SUM(cpd.paymentSplitAmount), DATE_FORMAT(cp.paymentDate, '%b-%Y')
FROM campaign_payment_detail cpd
JOIN campaign_payment cp ON cp.paymentId = cpd.paymentId
JOIN product ON cpd.productId = product.productId
WHERE cp.campaignId = 2413
GROUP BY cpd.productId, DATE_FORMAT(cp.paymentDate, '%b-%Y')
ORDER BY cp.paymentDate DESC, cpd.productId ASC
edit: Using DATE_FORMAT to format the date like you want.
First, based on the query you provided and without other information, the table product is useless..
I will do that:
select
cpd.paymentId,
SUM(cpd.paymentSplitAmount) as total,
cp.campaignId,
cp.paymentDate
from campaign_payment_detail cpd
inner join
campaign_payment cp on cp.paymentId = cpd.paymentId
where
cp.campaignId = 2413
GROUP BY cpd.productId, cp.paymentDate
ORDER BY cpd.paymentId ASC, cp.paymentDate DESC
You already mentioned 'grouping'. For that you need to add group by to your query, to group the data by productid and month, then you can add sum to sum the paymentSplitAmount.
The grouping syntax isn't wrong, it is missing completely. :)
select
cpd.productId, sum(cpd.paymentSplitAmount) as total, date_format(cp.paymentDate, '%m-%Y')
from campaign_payment_detail cpd
inner join campaign_payment cp on cp.paymentId = cpd.paymentId
inner join product on cpd.productId = product.productId
where
cp.campaignId = 2413
group by cp.productId, date_format(cp.paymentDate, '%m-%Y')
order by date_format(cp.paymentDate, '%m-%Y') desc, cp.productId
This assumes cp.paymentDate already contains the months. If not, you will have to round each date to the first of the month and group by that.
Now groups by month.

SQL for my requirement?

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.