Combining two similar queries together - mysql

I have MySQL queries both of which work fine independantly which I would like to combine together so I get three values returned.
Query 1 checks how many accounts have been deleted:
SELECT
COUNT(1) AS deleted_count,
SUBDATE(e.timestamp, INTERVAL WEEKDAY(e.timestamp) DAY) AS display_date
FROM
exit_reasons e
WHERE
e.timestamp>='$sixmonths'
GROUP BY
WEEKOFYEAR(e.timestamp)
ORDER BY
display_date ASC
LIMIT 26
This returns a date and the number who deleted in that week
Query 2 checks how many of these have subsequently signed up again:
SELECT
COUNT(1) AS date_count,
SUBDATE(e.timestamp, INTERVAL WEEKDAY(e.timestamp) DAY) AS display_date
FROM
exit_reasons e
LEFT JOIN
companies c on e.email=c.email
WHERE
e.timestamp>='$sixmonths' AND c.email IS NOT NULL
GROUP BY
WEEKOFYEAR(e.timestamp)
ORDER BY
display_date ASC
LIMIT 26
This returns a date and the number of that weeks deleted who now have a new account
I would like it to return a date and then the number deleted and number rejoined in one query so I tried:
SELECT
COUNT(1) AS date_count,
SUBDATE(e.timestamp, INTERVAL WEEKDAY(e.timestamp) DAY) AS display_date,
date_count as rejoined_count from
(SELECT
COUNT(1) AS date_count,
SUBDATE(e.timestamp, INTERVAL WEEKDAY(e.timestamp) DAY) AS display_date
FROM
exit_reasons e2
LEFT JOIN
companies c on e.email=c.email
LEFT JOIN
companies_users cu on e.email=cu.email
WHERE
e2.timestamp>='$sixmonths' AND c.email IS NOT NULL
GROUP BY
WEEKOFYEAR(e.timestamp)
ORDER BY
display_date ASC
LIMIT 26)
FROM
exit_reasons e
WHERE
e.timestamp>='$sixmonths'
GROUP BY
WEEKOFYEAR(e.timestamp)
ORDER BY
display_date ASC
LIMIT 26
but I am getting a syntax error - how can I combine these queries together into one query?

You should be able to combine the two queries into a single query by using an aggregate function along with some conditional logic like a CASE expression:
SELECT
COUNT(1) AS deleted_count,
SUM(CASE WHEN c.email IS NOT NULL THEN 1 ELSE 0 END) as date_count,
SUBDATE(e.timestamp, INTERVAL WEEKDAY(e.timestamp) DAY) AS display_date
FROM exit_reasons e
LEFT JOIN companies c
on e.email=c.email
WHERE e.timestamp>='$sixmonths'
GROUP BY WEEKOFYEAR(e.timestamp)
ORDER BY display_date ASC
LIMIT 26;
See Demo. Your check on the second query if the c.email IS NOT NULL is moved into the SUM(CASE.. which allows you to get a total of the rows that are not null.

I think the following will do what you want:
SELECT COUNT(*) AS deleted_count,
COUNT(c.email) as date_count,
SUBDATE(e.timestamp, INTERVAL WEEKDAY(e.timestamp) DAY) AS display_date
FROM exit_reasons e LEFT JOIN
companies c
on e.email = c.email
WHERE e.timestamp >= '$sixmonths'
GROUP BY WEEKOFYEAR(e.timestamp)
ORDER BY display_date ASC
LIMIT 26;
In the event that someone can sign up more than once with the same email, you should change the count() to use distinct:
COUNT(DISTINCT e.email) as deleted_count,
COUNT(DISTINCT c.email) as date_count

Related

How to set default value from mysql join interval yearmonth

I have problem with my query. I have two tables and I want join them to get the results based on primary key on first table, but I missing 1 data from first table.
this my fiddle
as you can see, I missing "xx3" from month 1
I have tried to change left and right join but, the results stil same.
So as you can see I have to set coalesce(sum(b.sd_qty),0) as total, if no qty, set 0 as default.
You should cross join the table to the distinct dates also:
SELECT a.item_code,
COALESCE(SUM(b.sd_qty), 0) total,
DATE_FORMAT(d.sd_date, '%m-%Y') month_year
FROM item a
CROSS JOIN (
SELECT DISTINCT sd_date
FROM sales_details
WHERE sd_date >= '2020-04-01' - INTERVAL 3 MONTH AND sd_date < '2020-05-01'
) d
LEFT JOIN sales_details b
ON a.item_code = b.item_code AND b.sd_date = d.sd_date
GROUP BY month_year, a.item_code
ORDER BY month_year, a.item_code;
Or, for MySql 8.0+, with a recursive CTE that returns the starting dates of all the months that you want the results, which can be cross joined to the table:
WITH RECURSIVE dates AS (
SELECT '2020-04-01' - INTERVAL 3 MONTH AS sd_date
UNION ALL
SELECT sd_date + INTERVAL 1 MONTH
FROM dates
WHERE sd_date + INTERVAL 1 MONTH < '2020-05-01'
)
SELECT a.item_code,
COALESCE(SUM(b.sd_qty), 0) total,
DATE_FORMAT(d.sd_date, '%m-%Y') month_year
FROM item a CROSS JOIN dates d
LEFT JOIN sales_details b
ON a.item_code = b.item_code AND DATE_FORMAT(b.sd_date, '%m-%Y') = DATE_FORMAT(d.sd_date, '%m-%Y')
GROUP BY month_year, a.item_code
ORDER BY month_year, a.item_code;
See the demo.

Getting the results of a query for each day in the past 30 days

Below is a query I run to extract some data in the past 24 hours.
SELECT
s.symbol,
count(cs.symbol_id) AS mentions
FROM symbols s
LEFT JOIN comments_symbols cs ON cs.symbol_id = s.id
LEFT JOIN comments c ON c.id = cs.comment_id
WHERE c.`date` > DATE_SUB(NOW(), INTERVAL 1 DAY)
GROUP BY (s.symbol)
ORDER BY mentions
DESC LIMIT 15
However, I need 24 hour intervals of data for the past 30 days in order to show a 30-day chart.
Instead of executing this query 30 times for the each day in the past 30 days, is there an approach I can take to do it with just one query execution?
It seems executing this query 30 times per page load may not be the best way to do this, no?
I hope I explained clearly, please let me know if any details are fuzzy.
Let me assume you have a list of dates. If you don't want to list them out, you can generate them:
with recursive dates as (
select curdate() - interval 30 day as dte
union all
select dte + interval 1 day
from dates
where dte < curdate()
)
Second, the LEFT JOIN seems superfluous, because you are filtering the results using LIMIT. However, I'll leave it in. Use a cross join to generate a row for each day and symbol . . . then aggregate:
SELECT s.symbol, COUNT(cs.symbol_id) AS mentions
FROM dates d CROSS JOIN
symbols s LEFT JOIN
comments_symbols cs
ON cs.symbol_id = s.id LEFT JOIN
comments c
ON c.id = cs.comment_id AND
c.date >= d.dte AND
c.date < d.date + interval 1 day
GROUP BY d.dte, s.symbol
ORDER BY d.dte, mentions DESC
Finally, to get 15 per day, let's put that into a CTE and use window functions:
WITH sm as (
SELECT d.dte, s.symbol, COUNT(cs.symbol_id) AS mentions
FROM dates d CROSS JOIN
symbols s LEFT JOIN
comments_symbols cs
ON cs.symbol_id = s.id LEFT JOIN
comments c
ON c.id = cs.comment_id AND
c.date >= d.dte AND
c.date < d.date + interval 1 day
GROUP BY d.dte, s.symbol
)
SELECT cs.*
FROM (SELECT cs.*,
ROW_NUMBER() OVER (PARTITION BY dte ORDER BY mentions DESC) as seqnum
FROM cs
) cs
WHERE seqnum <= 15;
ORDER BY dte, mentions DESC;

MySQL query with join or subquery

I have such a schema and queries:
http://sqlfiddle.com/#!2/7b032/3
Seperately I have these queries:
SELECT COUNT(*) AS 'times', userid, name
FROM main
WHERE comedate <= DATE_SUB(CURDATE(),
INTERVAL 5 DAY)
GROUP BY userid ORDER BY times DESC LIMIT 0,2;
SELECT * FROM details WHERE 1;
By comparing userid columns of both table I need to join them.
I need an output having these columns:
"times, userid, name, age, location"
Also order, group and limits should be considered.
I would be happy if you can write one query with JOIN and one query with subquery.
I have a 60k table and I will compare the performances.
How about this:
select x.times,
x.userid,
x.name,
d.age,
d.location
from
(
SELECT COUNT(*) AS 'times', userid, name
FROM main
WHERE comedate <= DATE_SUB(CURDATE(),
INTERVAL 5 DAY)
GROUP BY userid
) x
left join details d
on x.userid = d.userid
see SQL Fiddle with Demo
edit:
select x.times,
x.userid,
x.name,
d.age,
d.location
from
(
SELECT COUNT(*) AS 'times', userid, name
FROM main
WHERE comedate <= DATE_SUB(CURDATE(),
INTERVAL 5 DAY)
GROUP BY userid
ORDER BY times DESC
LIMIT 0,2
) x
left join details d
on x.userid = d.userid
see SQL Fiddle with demo

Looking to select a row from the database using DATETIME

I have the following query
SELECT * FROM ".TBL_FOOT_GAMES." ORDER BY id DESC LIMIT 1
I need to add a WHERE clause on the field date_confirmed.
date_confirmed is a DATETIME type.
I need to select only rows that are within 7 days of the current moment.
MORE CODE
SELECT g.home_user, g.away_user, g.home_score, g.away_score, g.id AS gameid, g.date_confirmed,
hu.username AS home_username, au.username AS away_username, ht.team AS home_team, at.team AS away_team
FROM tbl_foot_games g INNER JOIN tbl_users hu ON hu.id = g.home_user INNER JOIN tbl_users au ON au.id = g.away_user
INNER JOIN tbl_foot_teams ht ON ht.id = g.home_team INNER JOIN tbl_foot_teams at ON at.id = g.away_team
WHERE (g.type = '1' OR g.type = '2' OR g.type = '3' OR g.type = '4') AND g.status = '3' AND g.date_confirmed BETWEEN NOW() AND DATE_SUB(NOW(), INTERVAL 50 WEEK)
ORDER BY g.id DESC LIMIT 1
The statement works fine until I add the WHERE clause for the 50 week interval.
Presuming only seven days in the future (it looks like you're going to list upcoming football games):
SELECT *
FROM `tbl`
WHERE `date_confirmed` BETWEEN NOW() AND DATE_ADD(NOW(), INTERVAL 1 WEEK)
ORDER BY `id` DESC
LIMIT 1
Please read the documentation first next time; the answers are all there.
... WHERE date_confirmed BETWEEN NOW() AND DATE_ADD(NOW(), INTERVAL 7 DAY) ...
Have a look at the NOW() and DATE_SUB() functions.
These should let you create a date 7 days ago, then in your where clause you can check that the datetime column is greater than this.
You can use the date_sub function of MySQL to see if the diff is 7 days or less.
SELECT * FROM ".TBL_FOOT_GAMES."
WHERE DATE_ADD(DATE_CONFIRMED, INTERVAL '7 00:00:00' DAYS_SECOND) >= TIMESTAMP(CURDATE())
ORDER BY id DESC LIMIT 1
If you are interested in seeing only 7 days of difference from current date (ignoring the time value), then you can use DATEDIFF function like this:
SELECT * FROM ".TBL_FOOT_GAMES."
WHERE DATEDIFF(CURDATE(), DATE_CONFIRMED) <= 7
ORDER BY id DESC LIMIT 1

Invalid operation: column "[column_name]" must appear in the GROUP BY clause or be used in an aggregate function;

I have read almost 10+ questions related to mine but no one worked in my case. As i have 3 tables in my DB and i am trying to calculate sale from them with respect to time (Yearly sale). where i need to GROUP BY my query by date_added. In MYSQL it worked fine and give me fine result but in redshift i am stuck.
MYSQL QUERY:
SELECT
MONTHNAME(o.date_added) AS MONTH,
YEAR(o.date_added) AS YEAR,
COUNT(o.order_id) AS orders,
FROM
order o
LEFT JOIN(
SELECT
op.order_id,
SUM(op.quantity) AS op_qty,
SUM(op.total) AS total,
SUM(op.cost) AS cost
FROM
order_product op
GROUP BY
op.order_id
) op
ON
op.order_id = o.order_id
LEFT JOIN(
SELECT
order_id,
SUM(IF(CODE = 'coupon', VALUE, 0)) AS coupon
FROM
order_total
WHERE
1
GROUP BY
order_id
) ot
ON
ot.order_id = o.order_id
WHERE
(
DATE(o.date_added) >= DATE_ADD(NOW(), INTERVAL - 24 MONTH) AND DATE(o.date_added) <=
DATE(NOW())) AND(o.order_status_id = '22' OR o.order_status_id = '23')
GROUP BY
MONTH(o.date_added),
YEAR(o.date_added)
ORDER BY
date_added ASC
LIMIT 0, 25
This MYSQL query working very fine but when i convert it to RedShift's POSTGRE format it gives me error of Invalid operation: column "o.date_added" must appear in the GROUP BY clause or be used in an aggregate function;
POSTGRES Query:
SELECT
EXTRACT(MONTH FROM o.date_added) AS month,
EXTRACT(YEAR FROM o.date_added) AS year,
COUNT(o.order_id) AS orders
FROM
orders o
LEFT JOIN
(
SELECT
op.order_id,
SUM(op.quantity) AS op_qty,
SUM(op.total) AS total,
SUM(op.cost) AS cost
FROM
order_product op
GROUP BY
op.order_id
) op
ON op.order_id = o.order_id
LEFT JOIN
(
SELECT
order_id,
SUM(CASE
WHEN CODE = 'coupon' THEN VALUE
ELSE 0
END) AS coupon
FROM
order_total
WHERE
1
GROUP BY
order_id
) ot
ON ot.order_id = o.order_id
WHERE
(
DATE(o.date_added) >= now() + interval '-24 month'
AND DATE(o.date_added) <= DATE(NOW())
)
AND (
o.order_status_id = '22'
OR o.order_status_id = '23'
)
GROUP BY
(EXTRACT(MONTH FROM o.date_added), EXTRACT(YEAR FROM o.date_added))
ORDER BY
o.date_added ASC LIMIT 25
Is there any syntax error in postgre query and also why i need to add o.date_added in GROUP BY
Your ORDER BY clause has o.date_added in it but your actual result set does not have it. The ORDER BY is done after the query is done.
You can use:
ORDER BY month asc, year asc LIMIT 25
Also, you can remove the extra parentheses from the GROUP BY:
GROUP BY EXTRACT(MONTH FROM o.date_added), EXTRACT(YEAR FROM o.date_added)
DB-Fiddle
See Redshift documentation for use of now() function. Use getdate() instead, as the now() seems to be deprecated.
A column in an ORDER BY clause in a query containing GROUP BY works as if it were mentioned in the SELECT clause.
You have
SELECT
MONTHNAME(o.date_added) AS MONTH,
YEAR(o.date_added) AS YEAR,
COUNT(o.order_id) AS orders,
FROM
order o
...
GROUP BY
MONTH(o.date_added),
YEAR(o.date_added)
ORDER BY
date_added ASC
LIMIT 0, 25
In MySQL this takes advantage of its notorious nonstandard handling of GROUP BY (read this. https://dev.mysql.com/doc/refman/8.0/en/group-by-handling.html)
What you want, to get a query that works in multiple DBMSs, is this instead.
ORDER BY
MONTH(o.date_added) ASC,
YEAR(o.date_added) ASC
Adding o.date_added to your GROUP BY clause is incorrect, because you are grouping not by the entire date, but by the month and year of the date.
I found the ERROR.I used SQL Workbench and get the Error about NOW() function that i was using in my POSTGRE query.
Simply, i juts replaced NOW() with CURRENT_DATE and things worked for me. Also, i get Error for LIMIT 0, 25 but in POSTGRE , they allow LIMIT 25 [OFFSET n] .
Now my query looks like:
SELECT
EXTRACT(MONTH FROM o.date_added) AS month,
EXTRACT(YEAR FROM o.date_added) AS year,
COUNT(o.order_id) AS orders
FROM
orders o
LEFT JOIN
(
SELECT
op.order_id,
SUM(op.quantity) AS op_qty,
SUM(op.total) AS total,
SUM(op.cost) AS cost
FROM
order_product op
GROUP BY
op.order_id
) op
ON op.order_id = o.order_id
LEFT JOIN
(
SELECT
order_id
FROM
order_total
WHERE
1
GROUP BY
order_id
) ot
ON ot.order_id = o.order_id
WHERE
(
DATE(o.date_added) >= CURRENT_DATE + interval '-24 month'
AND DATE(o.date_added) <= DATE(CURRENT_DATE)
)
GROUP BY EXTRACT(MONTH FROM o.date_added), EXTRACT(YEAR FROM o.date_added)
ORDER BY year ASC, month ASC LIMIT 25
NOTE: IF YOU ARE WORKING ON REDSHIFT. IT DO NOT BERIEF ABOUT ERROR. I RECOMMEND TO USE SQL WORKBENCH. IT EXPLAIN ABOUT ERROR.