I have a table with data of guests to an event. I can retrieve the number of people who have attended (membersAttended) with this query:
SELECT COUNT(DISTINCT g.`name1`) AS `membersAttended`
FROM `tmc_doorapp_guests` g
LEFT JOIN `tmc_doorapp_events` e
ON e.`id` = g.`event_id`
WHERE `name1` REGEXP '^[0-9]+$'
AND e.`date` > DATE_SUB(NOW(), INTERVAL 30 DAY)
AND g.`checkin` = 1;
To retrieve the total number of members (totalMembers) I use:
SELECT COUNT(DISTINCT `id`) AS `totalMembers`
FROM `tmc_users`;
To get a percentage value (totalPercent) of members who have attended across the total members, I use PHP to make the two queries and then calculate like so:
$totalPercent = number_format(($membersAttended/$totalMembers*100),2);
I've tried combining the two queries and the percentage calculation in a similar way to below, with no luck:
SELECT COUNT(DISTINCT g.`name1`) AS `membersAttended`,
COUNT(DISTINCT m.`id`) AS `totalMembers`,
(`membersAttended` * 100 / `totalMembers`) AS `totalPercent`
FROM `tmc_doorapp_guests` g, `tmc_users` m
LEFT JOIN `tmc_doorapp_events` e
ON e.`id` = g.`event_id`
WHERE `name1` REGEXP '^[0-9]+$'
AND e.`date` > DATE_SUB(NOW(), INTERVAL 30 DAY)
AND g.`checkin` = 1;
I also tried expanding out the columns:
SELECT COUNT(DISTINCT g.`name1`) AS `membersAttended`,
COUNT(DISTINCT m.`id`) AS `totalMembers`,
(COUNT(DISTINCT g.`name1`) * 100 / COUNT(DISTINCT m.`id`)) AS `totalPercent`
FROM `tmc_doorapp_guests` g, `tmc_users` m
LEFT JOIN `tmc_doorapp_events` e
ON e.`id` = g.`event_id`
WHERE `name1` REGEXP '^[0-9]+$'
AND e.`date` > DATE_SUB(NOW(), INTERVAL 30 DAY)
AND g.`checkin` = 1;
This gives me the error:
Error code: 1054. Unknown column 'g.event_id' in 'on clause'
How can I combine these SQL/PHP queries/calculations into one SQL query?
You could use subquery:
SELECT COUNT(DISTINCT g.`name1`) AS `membersAttended`,
(SELECT COUNT(DISTINCT `id`) FROM `tmc_users`) AS `totalMembers`,
COUNT(DISTINCT g.`name1`) * 100 / (SELECT COUNT(DISTINCT `id`)
FROM `tmc_users`) AS `totalPercent`
FROM `tmc_doorapp_guests` g
LEFT JOIN `tmc_doorapp_events` e
ON e.`id` = g.`event_id`
WHERE `name1` REGEXP '^[0-9]+$'
AND e.`date` > DATE_SUB(NOW(), INTERVAL 30 DAY)
AND g.`checkin` = 1;
Related
I am having a trouble with a query.
I need to collect 2 companies from trade_directory where they have status 1 and match 3 categories.
I want to select the 2 companies on the least view first basis counted from company_views within the last 30 days.
SELECT
b.id, b.v_date, b.c_id COUNT(b.c_id) AS v_count
AND b.v_date >= DATE_ADD(NOW(), INTERVAL - 30 DAY)
FROM
company_views b
LEFT JOIN trade_directory v ON v.id = b.c_id
WHERE
v.cat1 = 'cat'
AND v.cat2 = 'rat'
AND v.cat3 = 'hat'
AND v.status = '1'
GROUP BY
b.c_id
ORDER BY
v_count ASC
LIMIT 2
Thanks
Chris
You're using AND operator in SELECT instead of WHERE
AND b.v_date >= DATE_ADD(NOW(), INTERVAL - 30 DAY)
can your try this code
SELECT
b.id, b.v_date, b.c_id COUNT(b.c_id) AS v_count
FROM
company_views b
LEFT JOIN trade_directory v ON v.id = b.c_id
WHERE
v.cat1 = 'cat'
AND v.cat2 = 'rat'
AND v.cat3 = 'hat'
AND v.status = '1'
AND b.v_date >= DATE_ADD(NOW(), INTERVAL - 30 DAY)
GROUP BY
b.c_id
ORDER BY
v_count ASC
LIMIT 2
I changed my approach (using different tables but here it what is working but it is really really slow)
SELECT o.*, COUNT(e.c_id) AS b_count
FROM cat3_entries o
LEFT JOIN company_views e ON e.c_id = o.c_id
AND e.v_date >= DATE_ADD(NOW(), INTERVAL - 30 DAY)
WHERE
o.c3_id = '81'
GROUP BY
o.c_id
ORDER BY
b_count ASC
LIMIT
3;
If I could resolve the speed issue this one would be great.
I have this query:
$sql = "SELECT m.id FROM members m
LEFT JOIN orders o ON o.user_id = m.id AND
YEAR(date) = YEAR(CURRENT_DATE - INTERVAL 1 MONTH) AND
MONTH(date) = MONTH(CURRENT_DATE - INTERVAL 1 MONTH)
LEFT JOIN transactions t ON t.user_id = o.user_id";
I need to get sum of transactions.points, so I do this:
$sql = "SELECT m.id, COALESCE(SUM(t.points), 0) AS total_points FROM members m
LEFT JOIN orders o ON o.user_id = m.id AND
YEAR(date) = YEAR(CURRENT_DATE - INTERVAL 1 MONTH) AND
MONTH(date) = MONTH(CURRENT_DATE - INTERVAL 1 MONTH)
LEFT JOIN transactions t ON t.user_id = o.user_id";
But when i execute this, I will get only rows with some transactions (and orders). What I must to do for getting 0 as total_points when no transactions found?
Text description: I want to get sum of points reward (from transactions table) of orders (orders table), where order was created in last month.
Thanks!
Get the sum of points per user and use it in the left join to get 0 when the user has no transactions.
SELECT m.id, COALESCE(t.total_points, 0) AS total_points
FROM members m
LEFT JOIN orders o ON o.user_id = m.id AND
/* use this instead of functions which prevent indexes on the date column from being used */
o.date >= date '2016-11-01' AND o.date <= date '2016-11-30'
LEFT JOIN (select user_id, SUM(points) total_points
from transactions group by user_id) t ON t.user_id = o.user_id
You need to add a GROUP BY clause to your query without which you are actually performing SUM() on the entire resultset. Not sure on which column but say on m.id
$sql = "SELECT m.id, COALESCE(SUM(t.points), 0) AS total_points FROM members m
LEFT JOIN orders o ON o.user_id = m.id AND
YEAR(date) = YEAR(CURRENT_DATE - INTERVAL 1 MONTH) AND
MONTH(date) = MONTH(CURRENT_DATE - INTERVAL 1 MONTH)
LEFT JOIN transactions t ON t.user_id = o.user_id
GROUP BY m.id";
I have 2 tables;
banner_views (id, b_id, b_date)- this record a banner view every time it gets displayed
banners_dynamic (id, status, static_iname, static_keywords, static_url, static_alt, static_type, static_image, b_views, b_clicks) - stores the banner data
I would like to select 3 banners_dynamic results which have had the least views in the last 7 days.
I did put somethign together (see below) but I realised it was grabbing the total views for all banner rather than uniquely by id.
SELECT *,
(SELECT COUNT(*) FROM banner_views v WHERE v.b_date >= DATE(NOW()) - INTERVAL 7 DAY) as post_count
FROM banners_dynamic b
WHERE static_keywords LIKE '%test%' AND b.status='1' AND b.static_type='1'
ORDER BY post_count ASC LIMIT 3
Can anyone point me in the correct direction?
You must join both banners_dynamic table and your subquery with corresponding banner IDs:
SELECT
b.*, p.b_count
FROM
banners_dynamic b
INNER JOIN (
SELECT
b_id,
COUNT(*) AS b_count
FROM
banner_views v
WHERE
v.b_date >= DATE(NOW() - INTERVAL 7 DAY)
GROUP BY
b_id
) p on p.b_id = b.id
WHERE
b.static_keywords LIKE '%test%'
AND b.`status` = '1'
AND b.static_type = '1'
ORDER BY
p.b_count ASC
LIMIT 3
UPDATE: You can do it even without subquery:
SELECT
b.*, COUNT(v.b_id) AS b_count
FROM
banners_dynamic b
INNER JOIN banner_views v ON v.b_id = b.id
WHERE
v.b_date >= DATE_ADD(NOW(), INTERVAL - 7 DAY)
AND b.static_keywords LIKE '%test%'
AND b.`status` = '1'
AND b.static_type = '1'
GROUP BY
v.b_id
ORDER BY
b_count ASC
LIMIT 3;
If you want to include banners without any views (count=0) then you must do a LEFT JOIN:
SELECT
b.*, COUNT(v.b_id) AS b_count
FROM
banners_dynamic b
LEFT JOIN banner_views v ON v.b_id = b.id
AND v.b_date >= DATE_ADD(NOW(), INTERVAL - 7 DAY)
WHERE
b.static_keywords LIKE '%test%'
AND b.`status` = '1'
AND b.static_type = '1'
GROUP BY
v.b_id
ORDER BY
b_count ASC
LIMIT 3;
I need to record the number of items requested and issued against each item every day. The purchase_doc table is:
The requested_items table contains item requested as follows:
The movement table contains item requested as follows:
The need output (data to be inserted) is:
One way of doing this is to fetch items issued and requested from the first 2 queries, and then build an array of items issued and requested against each item id, and then insert these values in the daily_movement table, like this:
SELECT n.item_id AS n__item_id, SUM(n.qty) AS qty
FROM requested_items n LEFT JOIN purchase_doc doc ON n.doc_id = doc.id
WHERE (doc.type = 'Item Request' AND doc.created_at > DATE_SUB(NOW(), INTERVAL 24 HOUR))
GROUP BY n.item_id
SELECT n.item_id AS item_id, SUM(n.qty) AS qty
FROM movement n LEFT JOIN purchase_doc doc ON n.doc_id = doc.id
WHERE (doc.type = 'Store Issue' AND doc.created_at > DATE_SUB(NOW(), INTERVAL 24 HOUR))
GROUP BY n.item_id
From these and other SELECTs, I need to insert a single row per item per day containing the qty of requests, issues, etc for this item in this fashion:
INSERT INTO daily_movement date, item_id, requested_qty, issued_qty VALUES ( NOW(), 23, 4, 5), ( NOW(), 25, 5, 5), ( NOW(), 113, 6, 8);
But there will be too many SELECTs (since I also need other activities performed per item), followed by an insert.
My question is: Is it possible to do this via a single SELECT ... INSERT statement. If not, can somebody suggest a more elegant way of doing this
I'm thinking this, but it might be over-simplified:
INSERT INTO `daily_movement`
(`date`, `item_id`, `requested_qty`, `issued_qty`)
SELECT NOW(), `r`.`item_id`, SUM(`r`.`qty`), SUM(`m`.`qty`)
FROM `purchase_doc` `d`
JOIN `requested_items` `r`
ON `r`.`doc_id` = `d`.`id`
LEFT JOIN `movement` `m`
ON `m`.`doc_id` = `d`.`id`
WHERE
(`d`.`type` = 'Item Request' OR `d`.`type` = 'Store Issue')
AND
`d`.`created_at` > DATE_SUB(NOW(), INTERVAL 24 HOUR)
GROUP BY `r`.`item_id`
EDIT
This is my final answer, with a nasty UNION to get around MySQL's lack of FULL OUTER JOIN:
INSERT INTO `daily_movement`
(`date`, `item_id`, `week_no`, `requested_qty`, `issued_qty`)
SELECT *
FROM (
(
SELECT COALESCE(`r`.`item_id`, `a`.`item_id`) AS `item_id`, CURDATE() AS `date`, NULL AS `week_no`, SUM(`r`.`qty`) AS `requests`, COALESCE(`a`.`issued`, 0) AS `issued`
FROM `purchase_doc` `d`
LEFT JOIN `requested_items` `r`
ON `r`.`doc_id` = `d`.`id`
LEFT JOIN (
SELECT `m`.`item_id`, SUM(`m`.`qty`) AS `issued`
FROM `purchase_doc` `d`
JOIN `movement` `m`
ON `m`.`doc_id` = `d`.`id`
WHERE `d`.`type` = 'Store Issue'
AND `d`.`created_at` > DATE_SUB(NOW(), INTERVAL 24 HOUR)
GROUP BY `m`.`item_id`
) `a`
ON `a`.`item_id` = `r`.`item_id`
WHERE `d`.`type` = 'Material Requisition'
AND `d`.`created_at` > DATE_SUB(NOW(), INTERVAL 24 HOUR)
GROUP BY `r`.`item_id`
) UNION DISTINCT (
SELECT COALESCE(`m`.`item_id`, `a`.`item_id`) AS `item_id`, CURDATE() AS `date`, NULL AS `week_no`, COALESCE(`a`.`requests`, 0) AS `requests`, SUM(`m`.`qty`) AS `issued`
FROM `purchase_doc` `d`
LEFT JOIN `movement` `m`
ON `m`.`doc_id` = `d`.`id`
LEFT JOIN (
SELECT `r`.`item_id`, SUM(`r`.`qty`) AS `requests`
FROM `purchase_doc` `d`
JOIN `requested_items` `r`
ON `r`.`doc_id` = `d`.`id`
WHERE `d`.`type` = 'Material Requisition'
AND `d`.`created_at` > DATE_SUB(NOW(), INTERVAL 24 HOUR)
GROUP BY `r`.`item_id`
) `a`
ON `a`.`item_id` = `m`.`item_id`
WHERE `d`.`type` = 'Store Issue'
AND `d`.`created_at` > DATE_SUB(NOW(), INTERVAL 24 HOUR)
GROUP BY `m`.`item_id`
)
ORDER BY `item_id`
) `u`
http://sqlfiddle.com/#!2/3923d/13
you can use the union operator here or here to get all your results in a single select
You can use a query like this -
edited:
INSERT INTO daily_movement(date, item_id, requested_qty, issued_qty)
SELECT i.item_id, SUM(ri.qty) requested_qty, SUM(m.qty) issued_qty FROM
(SELECT item_id FROM requested_items UNION SELECT item_id FROM movement) i
LEFT JOIN (
SELECT n.item_id, n.qty
FROM requested_items n LEFT JOIN purchase_doc doc ON n.doc_id = doc.id
WHERE doc.type = 'Item Request' AND doc.created_at > DATE_SUB(NOW(), INTERVAL 24 HOUR)
) ri
ON ri.item_id = i.item_id
LEFT JOIN (
SELECT n.item_id, n.qty
FROM movement n LEFT JOIN purchase_doc doc ON n.doc_id = doc.id
WHERE doc.type = 'Store Issue' AND doc.created_at > DATE_SUB(NOW(), INTERVAL 24 HOUR)
) m
ON m.item_id = i.item_id
GROUP BY
i.item_id;
I'm trying to take data from three different tables and output it using as few queries and as little PHP code as possible.
Listed below are the tables I have and the columns in each (only listing relevant columns).
exp_members (A)
columns: member_id, group_id
exp_brandrelations (B)
columns: member_id, brand_id
exp_du_mktgmats (C)
columns: du_id, brand_id, date
I want to loop through the members who belong to group_id='5' (from A), determine which brands are assigned to each member (from B), and get a list of du_ids (from C) that correspond to each member, that have been INSERTed in the last 24 hours.
So far, I can get a list of members in group 5:
SELECT member_id, brand_id FROM exp_brandrelations
WHERE member_id IN (SELECT member_id FROM exp_members where group_id = 5)
And I can get a list of du_ids from the last 24 hours:
SELECT du_id FROM exp_du_mktgmats
WHERE date >= DATE_SUB(NOW(), INTERVAL 1 DAY)
But I'm not sure how best to tie it all together.
This should do it!
SELECT m.member_id, b.brand_id, d.du_id FROM exp_members m, exp_brandrelations b, exp_du_mktgmats d WHERE m.group_id = '5' AND m.member_id = b.member_id AND b.brand_id = d.brand_id AND d.date >= DATE_SUB(NOW(), INTERVAL 1 DAY)
SELECT du_id FROM exp_members m, exp_brandrelations r, exp_du_mktgmats a
WHERE a.brand_id=r.brand_id AND r.member_id=m.member_id
AND date >= DATE_SUB(NOW(), INTERVAL 1 DAY)
AND m.group_id='5'
SELECT
c.du_id
FROM
exp_du_mktgmats c
LEFT JOIN
exp_brandrelations b
ON
c.brand_id = b.brand_id
LEFT JOIN
exp_members a
ON
b.member_id = a.member_id
WHERE
a.group_id = 5
AND
c.date >= DATE_SUB(NOW(), INTERVAL 1 DAY);