Query error in WHERE clause - mysql

SELECT product_key.contact_email, product_key.client_name, product_key.status, product_key.key, payment.paymentdate, product_key.id, MAX(paymentdate) AS latest_payment, DATE_ADD(MAX(paymentdate), INTERVAL 1 MONTH) AS expiration_date
FROM product_key LEFT OUTER JOIN payment ON payment.keyid=product_key.id
WHERE product_key.status = 'purchased' AND expiration_date = DATE_ADD(NOW(), INTERVAL 10 DAY) GROUP BY product_key.id
ORDER BY client_name asc
This is my query. I know I can't use an alias in a WHERE Clause since WHERE is first read before the SELECT. But even if I use something like this:
SELECT product_key.client_name, DATE_ADD(MAX(paymentdate), INTERVAL 1 MONTH) AS expiration_date
FROM product_key LEFT OUTER JOIN payment ON payment.keyid=product_key.id
WHERE DATE_ADD(MAX(paymentdate), INTERVAL 1 MONTH) = DATE_ADD(NOW(), INTERVAL 10 DAY) AND product_key.status = 'purchased'
GROUP BY product_key.id
ORDER BY client_name asc
Still an error. Please help. Thanks.

Since you're using an aggregate MAX() you should put it HAVING clause instead of WHERE. Assuming that other than that your query is correct and functional you can rewrite it like this
SELECT product_key.client_name,
DATE_ADD(MAX(paymentdate), INTERVAL 1 MONTH) AS expiration_date
FROM product_key LEFT OUTER JOIN payment
ON payment.keyid=product_key.id
AND product_key.status = 'purchased'
GROUP BY product_key.id
HAVING DATE_ADD(MAX(paymentdate), INTERVAL 1 MONTH) = DATE_ADD(NOW(), INTERVAL 10 DAY)
ORDER BY client_name
or
SELECT product_key.client_name,
DATE_ADD(MAX(paymentdate), INTERVAL 1 MONTH) AS expiration_date
FROM product_key LEFT OUTER JOIN payment
ON payment.keyid=product_key.id
AND product_key.status = 'purchased'
GROUP BY product_key.id
HAVING expiration_date = DATE_ADD(NOW(), INTERVAL 10 DAY)
ORDER BY client_name

You cannot use an alias defined in the select clause in a where clause. But that is ok, because you want to use having clause:
SELECT pk.contact_email, pk.client_name, pk.status, pk.key, p.paymentdate, pk.id,
MAX(paymentdate) AS latest_payment,
DATE_ADD(MAX(paymentdate), INTERVAL 1 MONTH) AS expiration_date
FROM product_key pk LEFT OUTER JOIN
payment p
ON p.keyid = pk.id
WHERE pk.status = 'purchased'
GROUP BY pk.id
HAVING expiration_date = DATE_ADD(NOW(), INTERVAL 10 DAY)
ORDER BY client_name asc;
You can use the alias in the having clause. Also, I added table aliases to your query to make it more readable.

Related

SQL QUERY double full join and a nested join

I have been struggling to make a single sql query that would give the result of all 3 queries in one. I suppose I would need to use full joins.
These 3 queries are all grouped by hour_key and store_id and ordered by hour_key.
The 3 queries are the following :
select SUM(qty_invoiced) as diag, DATE_FORMAT(a.created_at, '%Y-%m-%d-%h') as hour_key, b.store_id
from sales_flat_order_item a inner join
sales_flat_order b
on (a.order_id = b.entity_id)
where a.created_at BETWEEN (NOW() - INTERVAL 7 DAY) AND NOW() AND
a.parent_item_id IS NULL AND
a.sku LIKE 'D-%' AND b.status in ('processing', 'complete', 'printed', 'ready_to_print', 'ready_to_ship')
GROUP BY DATE_FORMAT(a.created_at, '%Y-%m-%d-%h'), b.store_id
ORDER BY hour_key DESC
select SUM(grand_total) as grand_total, store_id , DATE_FORMAT(created_at, '%Y-%m-%d-%h') as hour_key, SUM(shipping_amount) as shipping
from sales_flat_order
where created_at BETWEEN (NOW() - INTERVAL 7 DAY) AND NOW() AND status in ('processing', 'complete', 'printed', 'ready_to_print', 'ready_to_ship')
GROUP BY DATE_FORMAT(created_at, '%Y-%m-%d-%h'), store_id
ORDER BY hour_key DESC
SELECT COUNT(a.entity_id) as totalPaniers, a.store_id, DATE_FORMAT(a.created_at, '%Y-%m-%d-%h') as hour_key
FROM sales_flat_quote a
WHERE a.created_at BETWEEN (NOW() - INTERVAL 7 DAY) AND NOW()
GROUP BY DATE_FORMAT(a.created_at, '%Y-%m-%d-%h'), a.store_id
ORDER BY hour_key DESC
We don't know what your table structure is, so my best guess is
SELECT
DATE_FORMAT(a.created_at, '%Y-%m-%d-%h') as hour_key,
COUNT(entity_id) as totalPaniers,
store_id,
SUM(grand_total) as grand_total,
SUM(shipping_amount) as shipping,
SUM(qty_invoiced) as diag
FROM
sales_flat_quote a
[LEFT] JOIN sales_flat_order b ON a.store_id=b.store_id
INNER JOIN sales_flat_order_item c ON c.order_id=b.entity_id
WHERE
a.created_at BETWEEN (NOW() - INTERVAL 7 DAY) AND NOW()
AND b.created_at BETWEEN (NOW() - INTERVAL 7 DAY) AND NOW()
AND c.created_at BETWEEN (NOW() - INTERVAL 7 DAY) AND NOW()
AND b.`status` IN ('processing', 'complete', 'printed', 'ready_to_print', 'ready_to_ship')
AND c.parent_item_id IS NULL
AND c.sku LIKE 'D-%'
GROUP BY
b.created_at, store_id
ORDER BY hour_key DESC

mysql union all with aliases, syntax error

Why do I get
Error in query (1064): Syntax error near 'as q2)' at line 7
with
SELECT SQL_NO_CACHE q1.d1, q1.a, q2.b, (q1.a-q2.b)/q1.a*100 as Percentage
FROM
(SELECT Date(date) d1, count(id_update) a
FROM vas_updates
WHERE date > date_sub(now(), interval 2 hour)
GROUP BY DATE(date)) as q1
UNION ALL
(SELECT date(date) as d2, count(id_update) as b
FROM vas_updates
WHERE date BETWEEN
date_sub(date_sub(now(), interval 1 day), interval 2 hour)
AND
date_sub(now(), interval 1 day) group by DATE(d2) ) as q2
Can't I use aliases with UNION?
UPDATE:
this query might have leftovers from another query, I was tyring to understand the syntax error first.
What I'm trying to calculate is the percentage increase or decrease of two sums which are the hits from the last 2 hours of today compared to same timeframe from yesterday.
the table has just id and datetime
I suspect you actually want a JOIN
Something like this:-
SELECT SQL_NO_CACHE q1.d1, q1.a, q2.b, (q1.a-q2.b)/q1.a*100 as Percentage
FROM
(
SELECT Date(date) d1, count(id_update) a
FROM vas_updates
WHERE date > date_sub(now(), interval 2 hour)
GROUP BY DATE(date)
) as q1
INNER JOIN
(
SELECT date(date) as d2, count(id_update) as b
FROM vas_updates
WHERE date BETWEEN date_sub(date_sub(now(), interval 1 day), interval 2 hour) AND date_sub(now(), interval 1 day)
group by DATE(d2)
) as q2
ON q1.d1 = q2.d2
EDIT
Checked your updated query and it IS a JOIN you need.
You can use a CROSS JOIN. You are returning 1 value from each sub query, and doing a calculation on those values:-
SELECT SQL_NO_CACHE q1.d1, q1.a, q2.b, (q1.a-q2.b)/q1.a*100 as Percentage
FROM
(
SELECT MIN(Date(date)) d1, count(id_update) a
FROM vas_updates
WHERE date > date_sub(now(), interval 2 hour)
) as q1
CROSS JOIN
(
SELECT MIN(Date(date)) d2, count(id_update) as b
FROM vas_updates
WHERE date BETWEEN
date_sub(date_sub(now(), interval 1 day), interval 2 hour)
AND
date_sub(now(), interval 1 day)
) as q2
CROSS JOIN gives you every combination of the rows. In this case you have 1 resulting record. I have just returned the MIN date to get a single date to display.
You Can't. A UNION operation does not allow you to use alias on subqueries as it is an operation that creates a single table.
Like this:
select 1 a, 2 b
union all
select 3 blah, 4 bleh
This will result in
a b
1 2
3 4
See it here: http://sqlfiddle.com/#!2/68b32/444
On this query you only have two fields no matters what is on the second query it will only parse the first one, check if the others querys has the same quantity of fields as the first and if they are of the same type. Name the UNIONed querys with alies is invalid.
So I think what you need is probably a JOIN OR just all the fields
So, your query would be something like:
SELECT SQL_NO_CACHE tbl.d1,
tbl.a,
tbl.b,
(tbl.a-tbl.b)/tbl.a*100 as Percentage
FROM (SELECT Date(date) d1,
count(id_update) a,
null d2,
null b
FROM vas_updates
WHERE date > date_sub(now(), interval 2 hour)
GROUP BY DATE(date)
UNION ALL
SELECT null d1,
null a
date(date) as d2,
count(id_update) as b
FROM vas_updates
WHERE date
BETWEEN date_sub(date_sub(now(), interval 1 day), interval 2 hour)
AND date_sub(now(), interval 1 day) group by DATE(d2)
) tbl
But this most likely will not make the calculations right. You can use the version that #Kickstart has provided you.
Query of the answer from #Kickstart
SELECT SQL_NO_CACHE q1.d1, q1.a, q2.b, (q1.a-q2.b)/q1.a*100 as Percentage
FROM
(
SELECT Date(date) d1, count(id_update) a
FROM vas_updates
WHERE date > date_sub(now(), interval 2 hour)
GROUP BY DATE(date)
) as q1
INNER JOIN
(
SELECT date(date) as d2, count(id_update) as b
FROM vas_updates
WHERE date BETWEEN date_sub(date_sub(now(), interval 1 day), interval 2 hour) AND date_sub(now(), interval 1 day)
group by DATE(d2)
) as q2
ON q1.d1 = q2.d2
I decided to put this answer to explain why you are using the UNION operation in a wrong way.
I think that #Kickstart is right,and you can try this.
SELECT SQL_NO_CACHE d, a
FROM
(SELECT Date(date) d, count(id_update) as a
FROM vas_updates
WHERE date > date_senter code hereub(now(), interval 2 hour)
GROUP BY DATE(date))
UNION ALL
(SELECT date(date) as d, count(id_update) as a
FROM vas_updates
WHERE date BETWEEN
date_sub(date_sub(now(), interval 1 day), interval 2 hour)
AND
date_sub(now(), interval 1 day) group by DATE(d2) )
I'm wrong,UPDATE, you can try like this
SELECT SQL_NO_CACHE q1.d1, q1.a, q2.b, (q1.a-q2.b)/q1.a*100 as Percentage
FROM
(
SELECT DATE_FORMAT(Date(date),'%H') as d1, count(id_update) a
FROM vas_updates
WHERE date > date_sub(now(), interval 2 hour)
GROUP BY DATE(date)
) as q1
INNER JOIN
(
SELECT DATE_FORMAT(Date(date),'%H') as d2, count(id_update) as b
FROM vas_updates
WHERE date BETWEEN date_sub(date_sub(now(), interval 1 day), interval 2 hour) AND date_sub(now(), interval 1 day)
group by DATE(d2)
) as q2
ON q1.d1 = q2.d2

Convert NOT IN query to better performance

I'm using MySQL 5.0, and I need to fine tune this query. Can anyone please tell me what tuning I can do in this?
SELECT DISTINCT(alert_master_id) FROM alert_appln_header
WHERE created_date < DATE_SUB(CURDATE(), INTERVAL (SELECT parameters FROM schedule_config WHERE schedule_name = "Purging_Config") DAY)
AND alert_master_id NOT IN (
SELECT DISTINCT(alert_master_id) FROM alert_details
WHERE end_date IS NULL AND created_date < DATE_SUB(CURDATE(), INTERVAL (SELECT parameters FROM schedule_config WHERE schedule_name = "Purging_Config") DAY)
UNION
SELECT DISTINCT(alert_master_id) FROM alert_sara_header
WHERE sara_master_id IN
(SELECT alert_sara_master_id FROM alert_sara_lines
WHERE end_date IS NULL) AND created_date < DATE_SUB(CURDATE(), INTERVAL (SELECT parameters FROM schedule_config WHERE schedule_name = "Purging_Config") DAY)
) LIMIT 5000;
The first thing that I'd do is rewrite the subqueries as joins:
SELECT h.alert_master_id
FROM alert_appln_header h
JOIN schedule_config c
ON c.schedule_name = 'Purging_Config'
LEFT JOIN alert_details d
ON d.alert_master_id = h.alert_master_id
AND d.end_date IS NULL
AND d.created_date < CURRENT_DATE - INTERVAL c.parameters DAY
LEFT JOIN (
alert_sara_header s
JOIN alert_sara_lines l
ON l.alert_sara_master_id = s.sara_master_id
)
ON s.alert_master_id = h.alert_master_id
AND s.end_date IS NULL
AND s.created_date < CURRENT_DATE - INTERVAL c.parameters DAY
WHERE h.created_date < CURRENT_DATE - INTERVAL c.parameters DAY
AND d.alert_master_id IS NULL
AND s.alert_master_id IS NULL
GROUP BY h.alert_master_id
LIMIT 5000
If it's still slow after that, re-examine your indexing strategy. I'd suggest indexes over:
alert_appln_header(alert_master_id,created_date)
schedule_config(schedule_name)
alert_details(alert_master_id,end_date,created_date)
alert_sara_header(sara_master_id,alert_master_id,end_date,created_date)
alert_sara_lines(alert_sara_master_id)
OK, this may be just a shot in the dark, but I think you don't need as many DISTINCT here.
SELECT DISTINCT(alert_master_id) FROM alert_appln_header
WHERE created_date < DATE_SUB(CURDATE(), INTERVAL (SELECT parameters FROM schedule_config WHERE schedule_name = "Purging_Config") DAY)
AND alert_master_id NOT IN (
-- removed distinct here --
SELECT alert_master_id FROM alert_details
WHERE end_date IS NULL AND created_date < DATE_SUB(CURDATE(), INTERVAL (SELECT parameters FROM schedule_config WHERE schedule_name = "Purging_Config") DAY)
UNION
-- removed distinct here --
SELECT alert_master_id FROM alert_sara_header
WHERE sara_master_id IN
(SELECT alert_sara_master_id FROM alert_sara_lines
WHERE end_date IS NULL)
AND created_date < DATE_SUB(CURDATE(), INTERVAL (SELECT parameters FROM schedule_config WHERE schedule_name = "Purging_Config") DAY)
) LIMIT 5000;
Since using the DISTINCT is very costly, try to avoid it. In the first WHERE clause you are checking for ids that are NOT within some result, so it shouldn't matter if in that result some ids appear more than once.

MySql SELECT Subqueries JOIN

I am trying to do all of these SELECT statements in all one query so I will be able to further it and group it. I believe I have to tell it to JOIN on TABLE1. I can tell you that it should be JOINing on the field called ITEM. I have tried dozens of JOIN statements none of which does the trick because I have two WHERE statements in my subqueries.
SELECT ITEM, DSI, LEADTIME,
(SELECT COUNT(ORDER_NUMBER) FROM SUBTABLE1 TR1 WHERE TRANS_DATE BETWEEN DATE_SUB(curdate(), INTERVAL 730 DAY) AND DATE_SUB(curdate(), INTERVAL 365 DAY))
as OLDORDERS,
(SELECT COUNT(ORDER_NUMBER) FROM SUBTABLE2 TR2 WHERE TRANS_DATE BETWEEN DATE_SUB(curdate(), INTERVAL 364 DAY) AND curdate())
as NEWORDERS
FROM TABLE1
Displays:
ITEM | DSI | LEADTIME | OLDORDERS | NEWORDERS
PROD-1 0 1 16036 38399
PROD-2 1 0 16036 38399
PROD-3 1 1 16036 38399
Again...I believe I need it to JOIN the field ITEM on the subqueries, but I do not know how to do this, any ideas?
You don't actually need a JOIN, per se; rather, you need to "correlate" your subqueries, so that they refer to data in their containing query.
You haven't given your exact table definitions, so I can't say for sure, but here's my guess at what you need:
SELECT item, dsi, leadtime,
( SELECT COUNT(order_number)
FROM subtable1
WHERE trans_date BETWEEN DATE_SUB(CURDATE(), INTERVAL 730 DAY)
AND DATE_SUB(CURDATE(), INTERVAL 365 DAY)
-- restrict to "current" record from TABLE1:
AND subtable1.item = table1.item
) as OLDORDERS,
( SELECT COUNT(order_number)
FROM subtable1
WHERE trans_date BETWEEN DATE_SUB(CURDATE(), INTERVAL 364 DAY)
AND CURDATE()
-- restrict to "current" record from table1:
AND subtable1.item = table1.item
) as NEWORDERS
FROM table1
;
That's assuming that table1.item is the primary key, and that subtable1.item is a foreign-key referring to it. Naturally you'll have to adjust the query if that's not the case.

mysql join same table different result set

I would like to combine different results from the same table as one big result.
SELECT host_name,stats_avgcpu,stats_avgmem,stats_avgswap,stats_avgiowait
FROM sar_stats,sar_hosts,sar_appgroups,sar_environments
WHERE stats_host = host_id
AND host_environment = env_id
AND env_name = 'Staging 2'
AND host_appgroup = group_id
AND group_name = 'Pervasive'
AND DATE(stats_report_time) = DATE_SUB(curdate(), INTERVAL 1 DAY)
SELECT AVG(stats_avgcpu),AVG(stats_avgmem),AVG(stats_avgswap),AVG(stats_avgiowait)
FROM sar_stats
WHERE stats_id = "stat_id of the first query" and DATE(stats_report_time)
BETWEEN DATE_SUB(curdate(), INTERVAL 8 DAY) and DATE_SUB(curdate(), INTERVAL 1 DAY)
SELECT AVG(stats_avgcpu),AVG(stats_avgmem),AVG(stats_avgswap),AVG(stats_avgiowait)
FROM sar_stats
WHERE stats_id = "stat_id of the first query" and DATE(stats_report_time)
BETWEEN DATE_SUB(curdate(), INTERVAL 31 DAY) and DATE_SUB(curdate(), INTERVAL 1 DAY)
Desired output would be something like ...
host_name|stats_avgcpu|stats_avgmem|stats_avgswap|stats_avgiowait|7daycpuavg|7daymemavg|7dayswapavg|7dayiowaitavg|30daycpuavg|30daymemavg|....etc
SQL Fiddle
http://sqlfiddle.com/#!8/4930b/3
It seems like this is what you want. I updated the first query to use proper ANSI JOIN syntax and then for the additional two queries they were joined via a LEFT JOIN on the stats_host field:
SELECT s.stats_host,
h.host_name,
s.stats_avgcpu,
s.stats_avgmem,
s.stats_avgswap,
s.stats_avgiowait,
s7.7dayavgcpu,
s7.7dayavgmem,
s7.7dayavgswap,
s7.7dayavgiowait,
s30.30dayavgcpu,
s30.30dayavgmem,
s30.30dayavgswap,
s30.30dayavgiowait
FROM sar_stats s
INNER JOIN sar_hosts h
on s.stats_host = h.host_id
INNER JOIN sar_appgroups a
on h.host_appgroup = a.group_id
and a.group_name = 'Pervasive'
INNER JOIN sar_environments e
on h.host_environment = e.env_id
and e.env_name = 'Staging 2'
LEFT JOIN
(
SELECT s.stats_host,
AVG(s.stats_avgcpu) AS '7dayavgcpu',
AVG(s.stats_avgmem) AS '7dayavgmem',
AVG(s.stats_avgswap) AS '7dayavgswap',
AVG(s.stats_avgiowait) AS '7dayavgiowait'
FROM sar_stats s
WHERE DATE(stats_report_time) BETWEEN DATE_SUB(curdate(), INTERVAL 8 DAY) AND DATE_SUB(curdate(), INTERVAL 1 DAY)
GROUP BY s.stats_host
) s7
on s.stats_host = s7.stats_host
LEFT JOIN
(
SELECT s.stats_host,
AVG(s.stats_avgcpu) AS '30dayavgcpu',
AVG(s.stats_avgmem) AS '30dayavgmem',
AVG(s.stats_avgswap) AS '30dayavgswap',
AVG(s.stats_avgiowait) AS '30dayavgiowait'
FROM sar_stats s
WHERE DATE(s.stats_report_time) BETWEEN DATE_SUB(curdate(), INTERVAL 31 DAY) AND DATE_SUB(curdate(), INTERVAL 1 DAY)
GROUP BY s.stats_host
) s30
on s.stats_host = s30.stats_host
WHERE DATE(s.stats_report_time) = DATE_SUB(curdate(), INTERVAL 1 DAY);
see SQL Fiddle with Demo