I want to merge two query to have one result - mysql

This is my first Query that it give me sum of actual and estimated expenses related to salaries
SELECT
d.y_ AS `salaries`
, IFNULL(SUM(d.actual),0) AS `Estimated`
, IFNULL(SUM(d.estimated),0) AS `Actual`
FROM (
SELECT estimated_type AS`y_`
,null AS `Estimated`
, sum(estimated_amount) AS `Actual`
FROM bacci.estimated_expenses_table it
WHERE it.estimated_year = '2017'
AND it.estimated_type = 'management'
GROUP
BY YEAR(it.estimated_year)
union all
SELECT Type_expenses_table AS`y_`
,SUM(it.Amount_expenses_table) AS `Estimated`
, null AS `Actual`
FROM bacci.expenses_table it
WHERE it.Date_expenses_table >= '2017-01-01'
AND it.Date_expenses_table < '2017-01-01' + INTERVAL 1 YEAR
AND it.Type_expenses_table = 'management'
GROUP
BY YEAR(it.Date_expenses_table)
union all
SELECT estimated_type AS`y_`
,null AS `Estimated`
, sum(estimated_amount) AS `Actual`
FROM bacci.estimated_expenses_table it
WHERE it.estimated_year = '2017'
AND it.estimated_type = 'staff salaries'
GROUP
BY YEAR(it.estimated_year)
union all
SELECT Type_expenses_table AS`y_`
,SUM(it.Amount_expenses_table) AS `Estimated`
, null AS `Actual`
FROM bacci.expenses_table it
WHERE it.Date_expenses_table >= '2017-01-01'
AND it.Date_expenses_table < '2017-01-01' + INTERVAL 1 YEAR
AND it.Type_expenses_table = 'staff salaries'
GROUP
BY YEAR(it.Date_expenses_table)
) d
GROUP
BY d.y_
ORDER
BY d.y_;
The result:
My second Query that it give me sum of actual and estimated expenses related to equipments
SELECT
d.y_ AS `Equipments`
, IFNULL(SUM(d.actual),0) AS `Estimated`
, IFNULL(SUM(d.estimated),0) AS `Actual`
FROM (
select estimated_type AS`y_`
,null AS `Estimated`
, sum(estimated_amount) AS `Actual`
FROM bacci.estimated_expenses_table it
WHERE it.estimated_year = '2017'
and it.estimated_type = 'vehicle rent'
GROUP
BY YEAR(it.estimated_year)
union all
SELECT Type_expenses_table AS`y_`
,SUM(it.Amount_expenses_table) AS `Estimated`
, null AS `Actual`
FROM bacci.expenses_table it
WHERE it.Date_expenses_table >= '2017-01-01'
AND it.Date_expenses_table < '2017-01-01' + INTERVAL 1 YEAR
and it.Type_expenses_table = 'vehicle rent'
GROUP
BY YEAR(it.Date_expenses_table)
union all
SELECT estimated_type AS`y_`
,null AS `Estimated`
, sum(estimated_amount) AS `Actual`
FROM bacci.estimated_expenses_table it
WHERE it.estimated_year = '2017'
and it.estimated_type = 'vehicle fuel'
GROUP
BY YEAR(it.estimated_year)
union all
SELECT Type_expenses_table AS`y_`
,SUM(it.Amount_expenses_table) AS `Estimated`
, null AS `Actual`
FROM bacci.expenses_table it
WHERE it.Date_expenses_table >= '2017-01-01'
AND it.Date_expenses_table < '2017-01-01' + INTERVAL 1 YEAR
and it.Type_expenses_table = 'vehicle fuel'
GROUP
BY YEAR(it.Date_expenses_table)
union all
SELECT estimated_type AS`y_`
,null AS `Estimated`
, sum(estimated_amount) AS `Actual`
FROM bacci.estimated_expenses_table it
WHERE it.estimated_year = '2017'
and it.estimated_type = 'generator fuel'
GROUP
BY YEAR(it.estimated_year)
union all
SELECT Type_expenses_table AS`y_`
,SUM(it.Amount_expenses_table) AS `Estimated`
, null AS `Actual`
FROM bacci.expenses_table it
WHERE it.Date_expenses_table >= '2017-01-01'
AND it.Date_expenses_table < '2017-01-01' + INTERVAL 1 YEAR
and it.Type_expenses_table = 'generator fuel'
GROUP
BY YEAR(it.Date_expenses_table)
) d
GROUP
BY d.y_
ORDER
BY d.y_;
The result:
I want to have Salaries under it management and staff salaries
than Equipments under it Vehicle Rent, Vehicle fuel and Generator Fuel etcetera

You could use UNION to merge those queries:
SELECT [First query]
UNION ALL
SELECT 'Equipments', 'Estimated', 'Actual'
UNION ALL
SELECT [Second query]
Please note that the Select after the first UNION ALL simply selects the headlines for your second query. You also could use UNION (w/o ALL), because it does not look like you're having duplicates.

If you have duplicate registries and dont want to see them, u can use UNION ALL. if u dont mind duplicate registries u can use UNION.
here is and example
SELECT [query_1]
UNION ALL
SELECT [query_2]
UNION ALL
SELECT [query_3]

Related

MySQL - Get users who have NO orders in current month but don have in previous, in one request

I need to get users count, grouped by user type (A,B,C) and every month (that exist in db) in current year - only for users who don't have paid orders (with total > 0) in every month (every row returned by SQL), but have orders (with total > 0) in any previous months (in any year, not just current). In other words this is inactive users, who placed some paid order before, but don't placed any new orders in current SQL request row month returned.
What I expect to get in results (values are just examples):
label user_type data
Nov B 2
Nov A 1
Nov C 3
Dec C 1
.... other months
This means that in December there are 5 users with user type A and 3 users with user type B and 0 users with user type C, who DON'T placed orders in December 2021, but placed orders sometime before December in any year.
Sample DB (two tables - users and orders) with SQL that show number users, by every user type, in every month, who placed orders in this month. Instead of just this simple results, I need to get users counts that DON'T placed orders in this month, but placed paid orders somewhere before.
https://dbfiddle.uk/?rdbms=mysql_5.6&fiddle=4c4fadf67bcdc7cc3443f46c387173df
I need SQL that will work with MySQL 5.7
Try this query to generate counts for all months x user type
SELECT
DATE_FORMAT(DATE(CONCAT_WS('-', YEAR(CURDATE()), months.mm, '01')), "%b") as label,
users.user_type,
SUM(
EXISTS (
SELECT 1
FROM orders
WHERE orders.user_id = users.userid
AND orders.`date` < DATE(CONCAT_WS('-', YEAR(CURDATE()), months.mm, '01'))
) AND NOT EXISTS (
SELECT 1
FROM orders
WHERE orders.user_id = users.userid
AND orders.`date` BETWEEN DATE(CONCAT_WS('-', YEAR(CURDATE()), months.mm, '01')) AND LAST_DAY(DATE(CONCAT_WS('-', YEAR(CURDATE()), months.mm, '01')))
)
) counts
FROM (
SELECT '01' mm
UNION SELECT '02' UNION SELECT '03' UNION SELECT '04' UNION SELECT '05'
UNION SELECT '06' UNION SELECT '07' UNION SELECT '08' UNION SELECT '09'
UNION SELECT '10' UNION SELECT '11' UNION SELECT '12'
) months
CROSS JOIN users
GROUP BY months.mm, users.user_type
demo
Test this query if it fits your needs
SELECT DATE_FORMAT(o.date, "%b") as label,
UPPER(u.user_type) as user_type,
COUNT(distinct o.user_id) as data FROM orders o
JOIN users u ON o.user_id = u.userid
WHERE DATE_FORMAT(o.date, "%Y") = "2021"
AND o.user_id NOT IN
(SELECT DISTINCT o1.user_id FROM orders o1 WHERE DATE_FORMAT(o1.date, "%b") = DATE_FORMAT(now(), "%b") AND YEAR(o1.date) = YEAR(now()) )
AND o.user_id IN
(SELECT DISTINCT o1.user_id FROM orders o1 WHERE (DATE_FORMAT(o1.date, "%c") < DATE_FORMAT(now(), "%c") OR YEAR(o1.date) < YEAR(now())))
GROUP BY DATE_FORMAT(o.date, "%Y %b"),
u.user_type HAVING SUM(o.total) > 0 ORDER BY o.date ASC
EDIT
The query below returns every month of the year
SELECT months.MONTH as label,
ifnull(UPPER(u.user_type), '-') as user_type,
COUNT(distinct o.user_id) as data
FROM (
SELECT 1 AS MONTH
UNION SELECT 2 AS MONTH
UNION SELECT 3 AS MONTH
UNION SELECT 4 AS MONTH
UNION SELECT 5 AS MONTH
UNION SELECT 6 AS MONTH
UNION SELECT 7 AS MONTH
UNION SELECT 8 AS MONTH
UNION SELECT 9 AS MONTH
UNION SELECT 10 AS MONTH
UNION SELECT 11 AS MONTH
UNION SELECT 12 AS MONTH
) as months
LEFT JOIN orders o
ON DATE_FORMAT(o.date, "%c") = months.MONTH
LEFT JOIN users u ON o.user_id = u.userid
WHERE (DATE_FORMAT(o.date, "%Y") = "2021" OR o.date IS NULL)
AND (
(
NOT EXISTS
(SELECT DISTINCT o1.user_id
FROM orders o1
WHERE
DATE_FORMAT(o1.date, "%b") = DATE_FORMAT(now(), "%b")
AND YEAR(o1.date) = YEAR(now())
AND o1.user_id = o.user_id
)
AND EXISTS
(SELECT DISTINCT o1.user_id
FROM orders o1
WHERE
(DATE_FORMAT(o1.date, "%c") < DATE_FORMAT(now(), "%c") OR YEAR(o1.date) < YEAR(now())) AND o1.user_id = o.user_id
)
)
OR o.user_id IS null OR u.userid IS NULL
)
GROUP BY months.MONTH, u.user_type ORDER BY months.MONTH ASC
This uses a similar approach to VeteranSlayer but it starts with the cross join between months and users followed by the left join to orders. It also uses ranges for the date comparisons instead of the functions. It may perform really badly but it should give the correct result -
SELECT
months.month AS `label`,
u.user_type,
COUNT(u.userid) AS `data`
FROM (
SELECT 'Jan' `month`, '2021-01-01' month_start, '2021-01-31' month_end UNION ALL
SELECT 'Feb', '2021-02-01', '2021-02-28' UNION ALL
SELECT 'Mar', '2021-03-01', '2021-03-31' UNION ALL
SELECT 'Apr', '2021-04-01', '2021-04-30' UNION ALL
SELECT 'May', '2021-05-01', '2021-05-31' UNION ALL
SELECT 'Jun', '2021-06-01', '2021-06-30' UNION ALL
SELECT 'Jul', '2021-07-01', '2021-07-31' UNION ALL
SELECT 'Aug', '2021-08-01', '2021-08-31' UNION ALL
SELECT 'Sep', '2021-09-01', '2021-09-30' UNION ALL
SELECT 'Oct', '2021-10-01', '2021-10-31' UNION ALL
SELECT 'Nov', '2021-11-01', '2021-11-30' UNION ALL
SELECT 'Dec', '2021-12-01', '2021-12-31'
) months
INNER JOIN users u
LEFT JOIN orders o
ON o.date BETWEEN months.month_start AND months.month_end
AND o.user_id = u.userid
WHERE o.user_id IS NULL
AND EXISTS (
SELECT DISTINCT o1.user_id
FROM orders o1
WHERE o1.date < months.month_start
AND o1.user_id = u.userid
)
GROUP BY months.month, u.user_type
ORDER BY months.month_start ASC, u.user_type ASC;
EDIT
The performance of these queries varies dramatically based on the scale of the dataset, the distribution of data and the indices. I have done some tests with many different index variations and the following test datasets. Note the random data created in the two tables can lead to wildly different performance. The dummy table referenced in the SELECTs of the INSERTs is just a random table with 1M rows.
CREATE TABLE `users` (
`id` int unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
`user_type` char(1) NOT NULL,
KEY `IDX_user_type` (`user_type`)
);
INSERT INTO users (user_type)
SELECT
CASE (FLOOR(RAND() * 3) + 1) WHEN 1 THEN 'A' WHEN 2 THEN 'B' ELSE 'C' END AS `user_type`
FROM dummy
LIMIT 1000;
CREATE TABLE orders (
`id` int UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
`user_id` int,
`date` DATE,
`total` DECIMAL(6,2),
KEY `IDX_user_id_date` (`user_id`,`date`)
);
INSERT INTO orders (user_id, date, total)
SELECT
(FLOOR(RAND() * 1000) + 1) AS `user_id`,
('2020-01-01' + INTERVAL FLOOR(RAND() * 685) + 1 DAY) AS `date`,
( (FLOOR(RAND() * 10) + 1) * 5) AS `total`
FROM dummy
LIMIT 100000;
The most significant performance difference across the queries came from adding -
KEY `IDX_user_id_date` (`user_id`,`date`)
and adding the user_type index gave a small but consistent improvement -
KEY `IDX_user_type` (`user_type`)
ProGu's query executed consistently with an average time of 1.466 sec. And my query was similarly consistent at 0.922 sec. Your mileage will vary!
I haven't included time's for VeteranSlayer's query as it returned radically different results.
EDIT 2
Repopulated the two tables with 50k users and 1M orders
TRUNCATE TABLE orders;
TRUNCATE TABLE users;
INSERT INTO users (user_type)
SELECT
CASE (FLOOR(RAND() * 3) + 1) WHEN 1 THEN 'A' WHEN 2 THEN 'B' ELSE 'C' END AS `user_type`
FROM (SELECT 1 FROM dummy LIMIT 50000) t;
INSERT INTO orders (user_id, date, total)
SELECT
(FLOOR(RAND() * 50000) + 1),
TIMESTAMPADD(SECOND, FLOOR(RAND() * TIMESTAMPDIFF(SECOND, '2016-01-01', '2021-12-13')), '2016-01-01'),
((FLOOR(RAND() * 50) + 1) * 5)
FROM (SELECT 1 FROM dummy LIMIT 1000000) t
ORDER BY date;
The resulting distribution of orders, by time and user_id, is quite even which is unlikely to be realistic so this test dataset grossly exacerbates any performance issues, I think.
I was surprised that by using my months table, ProGu's query was significantly faster, dropping from 21.062sec to 9.703sec, and using one less temporary table (two instead of three).
SELECT
months.month as label,
users.user_type,
SUM(
EXISTS (
SELECT 1
FROM orders
WHERE orders.user_id = users.id
AND orders.`date` < months.month_start
) AND NOT EXISTS (
SELECT 1
FROM orders
WHERE orders.user_id = users.id
AND orders.`date` BETWEEN months.month_start AND months.month_end
)
) counts
FROM (
SELECT 'Jan' `month`, '2021-01-01' month_start, '2021-01-31' month_end UNION ALL
SELECT 'Feb', '2021-02-01', '2021-02-28' UNION ALL
SELECT 'Mar', '2021-03-01', '2021-03-31' UNION ALL
SELECT 'Apr', '2021-04-01', '2021-04-30' UNION ALL
SELECT 'May', '2021-05-01', '2021-05-31' UNION ALL
SELECT 'Jun', '2021-06-01', '2021-06-30' UNION ALL
SELECT 'Jul', '2021-07-01', '2021-07-31' UNION ALL
SELECT 'Aug', '2021-08-01', '2021-08-31' UNION ALL
SELECT 'Sep', '2021-09-01', '2021-09-30' UNION ALL
SELECT 'Oct', '2021-10-01', '2021-10-31' UNION ALL
SELECT 'Nov', '2021-11-01', '2021-11-30' UNION ALL
SELECT 'Dec', '2021-12-01', '2021-12-31'
) months
CROSS JOIN users
GROUP BY months.month, users.user_type
ORDER BY months.month_start ASC, users.user_type ASC
My query above can be significantly improved by pre grouping the orders data for the current year (your mileage will vary but worth considering) -
SELECT
months.month AS `label`,
u.user_type,
COUNT(u.id) AS `data`
FROM (
SELECT 'Jan' `month`, '2021-01-01' month_start, '2021-01-31' month_end UNION ALL
SELECT 'Feb', '2021-02-01', '2021-02-28' UNION ALL
SELECT 'Mar', '2021-03-01', '2021-03-31' UNION ALL
SELECT 'Apr', '2021-04-01', '2021-04-30' UNION ALL
SELECT 'May', '2021-05-01', '2021-05-31' UNION ALL
SELECT 'Jun', '2021-06-01', '2021-06-30' UNION ALL
SELECT 'Jul', '2021-07-01', '2021-07-31' UNION ALL
SELECT 'Aug', '2021-08-01', '2021-08-31' UNION ALL
SELECT 'Sep', '2021-09-01', '2021-09-30' UNION ALL
SELECT 'Oct', '2021-10-01', '2021-10-31' UNION ALL
SELECT 'Nov', '2021-11-01', '2021-11-30' UNION ALL
SELECT 'Dec', '2021-12-01', '2021-12-31'
) months
INNER JOIN users u
LEFT JOIN (
SELECT `user_id`, DATE_FORMAT(`date`, '%Y-%m-01') AS `m`
FROM `orders`
WHERE `date` >= '2021-01-01'
GROUP BY `user_id`, `m`
) o
ON o.m = months.month_start
AND o.user_id = u.id
WHERE o.user_id IS NULL
AND EXISTS (
SELECT 1
FROM orders o1
WHERE o1.date < months.month_start
AND o1.user_id = u.id
)
GROUP BY months.month, u.user_type
ORDER BY months.month_start ASC, u.user_type ASC
Execution time dropped from 12.422sec to 6.497sec
And the final test I tried was de-normalising by adding first_order_date to the users table -
ALTER TABLE `users` ADD COLUMN `first_order_date` DATE NULL AFTER `user_type`;
UPDATE users u
INNER JOIN (SELECT o.user_id, MIN(date) AS `first_o`, MAX(date) AS `last_o` FROM orders o GROUP BY o.user_id) t ON u.id = t.user_id
SET `u`.`first_order_date` = `t`.`first_o`, `u`.`last_order_date` = `t`.`last_o`;
I then modified my query to use this instead of the EXISTS sub-query -
SELECT
`months`.`month` AS `label`,
`u`.`user_type`,
COUNT(`u`.`id`) AS `data`
FROM (
SELECT 'Jan' `month`, '2021-01-01' month_start, '2021-01-31' month_end UNION ALL
SELECT 'Feb', '2021-02-01', '2021-02-28' UNION ALL
SELECT 'Mar', '2021-03-01', '2021-03-31' UNION ALL
SELECT 'Apr', '2021-04-01', '2021-04-30' UNION ALL
SELECT 'May', '2021-05-01', '2021-05-31' UNION ALL
SELECT 'Jun', '2021-06-01', '2021-06-30' UNION ALL
SELECT 'Jul', '2021-07-01', '2021-07-31' UNION ALL
SELECT 'Aug', '2021-08-01', '2021-08-31' UNION ALL
SELECT 'Sep', '2021-09-01', '2021-09-30' UNION ALL
SELECT 'Oct', '2021-10-01', '2021-10-31' UNION ALL
SELECT 'Nov', '2021-11-01', '2021-11-30' UNION ALL
SELECT 'Dec', '2021-12-01', '2021-12-31'
) `months`
INNER JOIN `users` `u`
LEFT JOIN (
SELECT `user_id`, DATE_FORMAT(`date`, '%Y-%m-01') AS `m`
FROM `orders`
WHERE `date` >= '2021-01-01'
GROUP BY `user_id`, `m`
) o
ON `o`.`m` = `months`.`month_start`
AND `o`.`user_id` = `u`.`id`
WHERE `o`.`user_id` IS NULL
AND `u`.`first_order_date` < `months`.`month_start`
GROUP BY `months`.`month`, `u`.`user_type`
ORDER BY `months`.`month_start` ASC, `u`.`user_type` ASC;
This returns the same result in 1.447sec. Obviously, de-normalising like this should be avoided but I included it here as it shows the performance benefit for this one scenario.

SQL where query to a first row of IN condition

I have this SQL query:
SELECT (CASE WHEN count(_days) > 0 THEN 'yes' ELSE 'No' END) as availability
FROM (
SELECT count(roomTypeDay2.room_type_id) as _days
FROM room_type_day as roomTypeDay2
LEFT JOIN room_type as roomType2 on roomTypeDay2.room_type_id = roomType2.id
WHERE roomType2.accommodation_id=3
AND roomTypeDay2.date IN ( '2018-06-09 00:00:00','2018-06-10 00:00:00','2018-06-11 00:00:00')
GROUP BY roomTypeDay2.room_type_id
HAVING COUNT(roomTypeDay2.room_type_id) = 3
) as disponible
This works fine, but now, I want to filter if the first date row (2018-06-09 00:00:00) have
"min_night_stay <= 3 and release_days <=2"
parameters, but I don't know how doing it.
I try adding these lines to a query:
AND (roomTypeDay2.date = '2018-06-09 00:00:00' and
roomTypeDay2.min_night_stay <= 3 AND roomTypeDay2.release_days <= 2)
But this is not correct query.
UDPATE
SELECT (CASE WHEN count(_days) > 0 THEN 'yes' ELSE 'No' END) as availability
FROM (
SELECT count(roomTypeDay2.room_type_id) as _days
FROM room_type_day as roomTypeDay2
LEFT JOIN room_type as roomType2 on roomTypeDay2.room_type_id = roomType2.id
AND roomType2.accommodation_id=3
WHERE roomTypeDay2.date IN ( '2018-06-09 00:00:00','2018-06-10 00:00:00','2018-06-11 00:00:00')
GROUP BY roomTypeDay2.room_type_id
HAVING COUNT(roomTypeDay2.room_type_id) = 3
) as disponible
Solution
SELECT (CASE WHEN count(_days) > 0 THEN 'yes' ELSE 'No' END) as availability
FROM (
SELECT count(roomTypeDay2.room_type_id) as _days
FROM room_type_day as roomTypeDay2
LEFT JOIN room_type as roomType2 on roomTypeDay2.room_type_id = roomType2.id
WHERE roomType2.accommodation_id=3
AND roomTypeDay2.num_rooms_available > 0
AND (roomTypeDay2.date = '2018-06-12 00:00:00' AND roomTypeDay2.min_night_stay <= 2 AND roomTypeDay2.release_days <= 2 )
OR roomTypeDay2.date IN ( '2018-06-13 00:00:00','2018-06-14 00:00:00')
GROUP BY roomTypeDay2.room_type_id
HAVING COUNT(roomTypeDay2.room_type_id) = 3
) as disponible
This is your WHERE clause:
WHERE roomTypeDay2.date IN ('2018-06-09 00:00:00',
'2018-06-10 00:00:00',
'2018-06-11 00:00:00')
Now for the first date you want additional criteria. Use AND and ORwith parentheses for this:
WHERE
(
roomTypeDay2.date = '2018-06-09 00:00:00'
AND
roomTypeDay2.min_night_stay <= 3
AND
roomTypeDay2.release_days <= 2
)
OR
roomTypeDay2.date IN ('2018-06-10 00:00:00','2018-06-11 00:00:00')
SELECT
(
CASE WHEN count(_days) > 0 THEN 'yes' ELSE 'No' END
) as availability
from
(
SELECT
count(roomTypeDay2.room_type_id) as _days
FROM
room_type_day as roomTypeDay2
LEFT JOIN room_type as roomType2 on roomTypeDay2.room_type_id = roomType2.id
AND roomType2.accommodation_id = 3
WHERE
roomTypeDay2.date IN ('2018-06-10 00:00:00', '2018-06-11 00:00:00')
GROUP BY
roomTypeDay2.room_type_id
HAVING
COUNT(roomTypeDay2.room_type_id) = 3
union
SELECT
count(roomTypeDay2.room_type_id) as _days
FROM
room_type_day as roomTypeDay2
LEFT JOIN room_type as roomType2 on roomTypeDay2.room_type_id = roomType2.id
AND roomType2.accommodation_id = 3
WHERE
roomTypeDay2.date = '2018-06-09 00:00:00'
and roomTypeDay2.min_night_stay <= 3
AND roomTypeDay2.release_days <= 2
GROUP BY
roomTypeDay2.room_type_id
HAVING
COUNT(roomTypeDay2.room_type_id) = 3
) disponible

Adding UNION to a complex SELECT in MySQL

I would like help adding a UNION to get all the months to a query. I tried adding union in a bunch of spots but just can't get it right.
SELECT MONTH(FirstPublishedToPortal) AS theMonth,
YEAR(FirstPublishedToPortal) AS theYear,
MONTHNAME(FirstPublishedToPortal) AS theMonthName,
COUNT(DISTINCT PONum) AS POCount,
SUM(unconfirmedEmailSent) AS unconfirmedEmailsSent,
( SELECT COUNT(DISTINCT PONum)
FROM POFlags
WHERE unconfirmedEmailSent = 0
AND MONTH(FirstPublishedToPortal) = theMonth
AND YEAR(FirstPublishedToPortal) = theYear
AND VendorNum = '2222'
) AS onTimeConfirmed,
SUM(lateEmailSent) AS lateEmailsSent,
( SELECT COUNT(DISTINCT PONum)
FROM POFlags
WHERE lateEmailSent = 0
AND MONTH(FirstPublishedToPortal) = theMonth
AND YEAR(FirstPublishedToPortal) = theYear
AND VendorNum = '2222'
) AS onTimePOCount
FROM POFlags
WHERE VendorNum = '2222'
AND FirstPublishedToPortal >= '2017-01-08'
GROUP BY theYear DESC, theMonth DESC
ORDER BY FirstPublishedToPortal DESC
Where do the union clauses go in this query?
I think there needs to be something like the following code but I don't understand where to put it to work correctly with the GROUP BY or ORDER BY.
(SELECT 1, null, 'January', 0, 0, 0, 0, 0)
UNION
(SELECT 2, null, 'February', 0, 0, 0, 0, 0)
UNION
(SELECT 3, null, 'March', 0, 0, 0, 0, 0)
.
.
(SELECT 12, null, 'December, 0, 0, 0, 0, 0)
SQL FIDDLE HERE
Use a left join based on a nested query with union.
Use Coalesce to apply your default values.
Move where condition on join clause
warning, 2017 is hardcoded
Something like this : http://sqlfiddle.com/#!9/458808/27
SELECT
all_month.nMonth AS theMonth,
coalesce(YEAR(FirstPublishedToPortal),2017) AS theYear,
all_month.sMonth AS theMonthName,
coalesce(COUNT(DISTINCT PONum),0) AS POCount,
coalesce(SUM(unconfirmedEmailSent),0) AS unconfirmedEmailsSent,
coalesce(( SELECT COUNT(DISTINCT PONum)
FROM POFlags
WHERE unconfirmedEmailSent = 0
AND MONTH(FirstPublishedToPortal) = theMonth
AND YEAR(FirstPublishedToPortal) = theYear
AND VendorNum = '2222'
),0) AS onTimeConfirmed,
coalesce(SUM(lateEmailSent),0) AS lateEmailsSent,
coalesce(( SELECT COUNT(DISTINCT PONum)
FROM POFlags
WHERE lateEmailSent = 0
AND MONTH(FirstPublishedToPortal) = theMonth
AND YEAR(FirstPublishedToPortal) = theYear
AND VendorNum = '2222'
),0) AS onTimePOCount
FROM (
(SELECT 1 nMonth, 'January' sMonth) UNION ALL
(SELECT 2 nMonth, 'February' sMonth ) UNION ALL
(SELECT 3 nMonth, 'March' sMonth ) UNION ALL
(SELECT 4 nMonth, 'April' sMonth ) UNION ALL
(SELECT 5 nMonth, 'May' sMonth ) UNION ALL
(SELECT 6 nMonth, 'June' sMonth ) UNION ALL
(SELECT 7 nMonth, 'July' sMonth ) UNION ALL
(SELECT 8 nMonth, 'August' sMonth ) UNION ALL
(SELECT 9 nMonth, 'September' sMonth) UNION ALL
(SELECT 10 nMonth, 'October' sMonth ) UNION ALL
(SELECT 11 nMonth, 'November' sMonth )UNION ALL
(SELECT 12 nMonth, 'December' sMonth)
) all_month left join POFlags on
MONTH(FirstPublishedToPortal)=nMonth and
VendorNum = '2222' and
FirstPublishedToPortal >= '2017-01-08'
GROUP BY
theYear DESC, nMonth DESC
ORDER BY
nMonth DESC
Another way exist using a global union between your orginal result and the default value, but this method require a second group by...

Different results from Identical databases

I'm having an issue where my live database (MariaDB) has the exact same data as my local(MySQL) but the following query is returning the same results but in a different order (I know im not the best at SQL so I'll apologise in advance):
SELECT
`products`.*
, CONCAT( `booking_inventory`.`year`, '-', LPAD( `booking_inventory`.`month`, 2, '00' ), '-', LPAD( `booking_inventory`.`day`, 2, '00' ) ) AS `order_date`
, `category_product`.`category_id` AS `pivot_category_id`
, `category_product`.`product_id` AS `pivot_product_id`
, `count_activate`.`active_count`
, IF( `count_activate`.`product_id` > 0, 0, 1 ) AS coming_soon
FROM
`products`
INNER JOIN
`category_product`
ON
`products`.`id` = `category_product`.`product_id`
LEFT JOIN
(
SELECT
inventory.*
FROM
inventory
INNER JOIN
`booking_inventory`
ON
`inventory`.`id` = `booking_inventory`.`inventory_id`
WHERE
CONCAT( `booking_inventory`.`year`, '-', LPAD( `booking_inventory`.`month`, 2, '00' ), '-', LPAD( `booking_inventory`.`day`, 2, '00' ) ) > NOW()
ORDER BY
DATE( CONCAT( `booking_inventory`.`year`, '-', LPAD( `booking_inventory`.`month`, 2, '00' ), '-', LPAD( `booking_inventory`.`day`, 2, '00' ) ) )
)
AS
inventory
ON
`products`.`id` = `inventory`.`product_id`
INNER JOIN
`booking_inventory`
ON
`inventory`.`id` = `booking_inventory`.`inventory_id`
LEFT JOIN
(
SELECT
COUNT(inventory.id) AS active_count
, product_id
FROM
inventory
INNER JOIN
`booking_inventory`
ON
`inventory`.`id` = `booking_inventory`.`inventory_id`
WHERE
status_id = 1
AND
(
stock > 0
OR stock = -1
)
AND
CONCAT( `booking_inventory`.`year`, '-', LPAD( `booking_inventory`.`month`, 2, '00' ), '-', LPAD( `booking_inventory`.`day`, 2, '00' ) ) > NOW()
GROUP BY
product_id
)
AS
count_activate
ON
`count_activate`.`product_id` = `products`.`id`
WHERE
`category_product`.`category_id` = 2
AND EXISTS
(
SELECT
*
FROM
`sites`
INNER JOIN
`product_site`
ON
`sites`.`id` = `product_site`.`site_id`
WHERE
`product_site`.`product_id` = `products`.`id`
AND
`status_id` = 1
AND
`site_id` = 1
)
AND
`products`.`status_id` = 1
AND
CONCAT( `booking_inventory`.`year`, '-', LPAD( `booking_inventory`.`month`, 2, '00' ), '-', LPAD( `booking_inventory`.`day`, 2, '00' ) ) > NOW()
GROUP BY
`products`.`id`
ORDER BY
`coming_soon` ASC
, `order_date` ASC
LIMIT 100
OFFSET 0
can anyone tell me whats causing this?
regards
(left id External right is Local)
EDIT
Thanks for the daft comments below and the down vote, very helpful.... After some digging, I have found the cause yet not the answer. In the second JOIN (inventory) the date ordering isn't returning the same results. If I order by the inventory id, price, SKU I get the same results across local and external data but not using the date... would anyone know why?
regards
The problem was down to the version/dbengine. The first left join has an order by in it, which I didn't realise didn't keep its order once used by the parent (depending on version/dbengine).
One way to overcome this is by setting a limit of 18446744073709551615 which forces the results to be stored in a temp table (or something, i know not what I speak!).
the other issue was inner join further down the query which forced to table to be reordered.
SELECT
IF( `counter`.`product_id` > 0, 0, 1 ) AS coming_soon,
bd.skill_level,
counter.active_count,
p.*
FROM
(
SELECT
p.*,
booking_inventory.inventory_id,
category_product.category_id,
CONCAT( `booking_inventory`.`year`, '-', LPAD( `booking_inventory`.`month`, 2, '00' ), '-', LPAD( `booking_inventory`.`day`, 2, '00' ) ) as date
FROM
booking_inventory
JOIN
inventory
ON
inventory.id = booking_inventory.inventory_id
LEFT JOIN
products AS p
ON
p.id = inventory.product_id
INNER JOIN
`category_product`
ON
`p`.`id` = `category_product`.`product_id`
WHERE
CONCAT( `booking_inventory`.`year`, '-', LPAD( `booking_inventory`.`month`, 2, '00' ), '-', LPAD( `booking_inventory`.`day`, 2, '00' ) ) > NOW()
AND
`category_product`.`category_id` = 2
ORDER BY
date
LIMIT
18446744073709551615
)
AS
p
LEFT JOIN
booking_data AS bd
ON
p.id = bd.product_id
LEFT JOIN
(
SELECT
COUNT(`inventory`.`id`) AS `active_count`,
`inventory`.`product_id`
FROM
`inventory`
INNER JOIN
`booking_inventory`
ON
`inventory`.`id` = `booking_inventory`.`inventory_id`
WHERE
`inventory`.`status_id` = 1
AND
(
`inventory`.`stock` > 0
OR
`inventory`.`stock` = -1
)
AND
CONCAT( `booking_inventory`.`year`, '-', LPAD( `booking_inventory`.`month`, 2, '00' ), '-', LPAD( `booking_inventory`.`day`, 2, '00' ) ) > NOW()
GROUP BY
`inventory`.`product_id`
)
AS
counter
ON
`counter`.`product_id` = `p`.`id`
WHERE
EXISTS
(
SELECT
*
FROM
`sites`
INNER JOIN
`product_site`
ON
`sites`.`id` = `product_site`.`site_id`
WHERE
`product_site`.`product_id` = `p`.`id`
AND
`status_id` = 1
AND
`site_id` = 1
)
GROUP BY
p.id
ORDER BY
coming_soon,
p.date,
p.name

simplify this query

SELECT COUNT( sendbook.id ) AS total, SUM( sendbook.num ) AS num, (
SELECT COUNT( sendbook.id )
FROM sendbook
INNER JOIN clients ON clients.id = sendbook.clientid
WHERE sendbook.issueid = '29'
AND clients.area >1000
AND clients.area <2000
) AS area1000, (
SELECT COUNT( sendbook.id )
FROM sendbook
INNER JOIN clients ON clients.id = sendbook.clientid
WHERE sendbook.issueid = '29'
AND clients.area >2000
AND clients.area <3000
) AS area2000, (
SELECT COUNT( sendbook.id )
FROM sendbook
INNER JOIN clients ON clients.id = sendbook.clientid
WHERE sendbook.issueid = '29'
AND clients.area >3000
AND clients.area <4001
) AS area2000
FROM `sendbook`
WHERE sendbook.issueid = '29'
total num area1000 area2000 area2000
8 438 3 3 2
Do you know a way to simplify this query?
Thinks
you need to use case statement something like this :
SELECT COUNT( sendbook.id ),
(CASE
WHEN clients.area between '1000' and '2000' THEN 1
WHEN clients.area between '2000' and '3000' THEN 2
WHEN clients.area between '3000' and '4000' THEN 3
END) AS myrange FROM mytable
GROUP BY myrange