MySQL How to select mulipie rows then send to another query - mysql

I hava a Table ,I can use
SELECT `event_id` FROM `event` WHERE 1
to get mulipie rows event_id , and I put event_id in this query
SET #event_id = XXXX;
SELECT * FROM(
(SELECT `event_id`,`author_id`,`latitude`,`longitude`,`type`,`rate`,`description`,`photo_id`, UNIX_TIMESTAMP(`timestamp`) AS `timestamp`
FROM `event`) AS `event`,
(SELECT COUNT(*) AS `thumb_up_count` FROM `thumb_event` WHERE `event_id` = #event_id AND `thumb_state` = 1) AS `thumb_up_count`,
(SELECT COUNT(*) AS `thumb_down_count` FROM `thumb_event` WHERE `event_id` = #event_id AND `thumb_state` = 2) AS `thumb_down_count`,
(SELECT IFNULL((SELECT `thumb_state` FROM `thumb_event` WHERE `event_id` = #event_id AND `user_id` = ?) , 0) AS `thumbed`) AS `thumbed`,
(SELECT COUNT(*) AS `comment_count` FROM `event_comment` WHERE `event_id` = #event_id) AS `comment_count`
) WHERE `event_id` = #event_id;
I can use SET #event_id = 0000; to set event_id , but how I can put all event_id from select in that query

If i understoods, maybe what you need it's an IN clause:
WHERE `event_id` IN (SELECT `event_id` FROM `event` WHERE 1);

Related

SQL query to select all rows with max column value

CREATE TABLE `user_activity` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL,
`type` enum('request','response') DEFAULT NULL,
`data` longtext NOT NULL,
`created_at` datetime DEFAULT NULL,
`source` varchar(255) DEFAULT NULL,
`task_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
);
I have this data:-
Now I need to select all rows for user_id=527 where created_at value is the maximum. So I need the last 3 rows in this image.
I wrote this query:-
SELECT *
FROM user_activity
WHERE user_id = 527
AND source = 'E1'
AND task_name IN ( 'GetReportTask', 'StopMonitoringUserTask' )
AND created_at = (SELECT Max(created_at)
FROM user_activity
WHERE user_id = 527
AND source = 'E1'
AND task_name IN ( 'GetReportTask',
'StopMonitoringUserTask' ));
This is very inefficient because I am running the exact same query again as an inner query except that it disregards created_at. What's the right way to do this?
I would use a correlated subquery:
SELECT ua.*
FROM user_activity ua
WHERE ua.user_id = 527 AND source = 'E1' AND
ua.task_name IN ('GetReportTask', 'StopMonitoringUserTask' ) AND
ua.created_at = (SELECT MAX(ua2.created_at)
FROM user_activity ua2
WHERE ua2.user_id = ua.user_id AND
ua2.source = ua.source AND
ua2.task_name IN ( 'GetReportTask', 'StopMonitoringUserTask' )
);
Although this might seem inefficient, you can create an index on user_activity(user_id, source, task_name, created_at). With this index, the query should have decent performance.
Order by created_at desc and limit your query to return 1 row.
SELECT *
FROM user_activity
WHERE user_id = 527
AND source = 'E1'
AND task_name IN ( 'GetReportTask', 'StopMonitoringUserTask' )
ORDER BY created_at DESC
LIMIT 1;
I used EverSQL and applied my own changes to come up with this single-select query that uses self-join:-
SELECT *
FROM user_activity AS ua1
LEFT JOIN user_activity AS ua2
ON ua2.user_id = ua1.user_id
AND ua2.source = ua1.source
AND ua2.task_name IN ( 'GetReportTask', 'StopMonitoringUserTask' )
AND ua1.created_at < ua2.created_at
WHERE ua1.user_id = 527
AND ua1.source = 'E1'
AND ua1.task_name IN ( 'GetReportTask', 'StopMonitoringUserTask' )
AND ua2.created_at IS NULL;
However, I noticed that the response times of both queries were similar. I tried to use Explain to identify any performance differences; and from what I understood from its output, there are no noticeable differences because proper indexing is in place. So for readability and maintainability, I'll just use the nested query.

Delete child records if the count exceeds 10

I have a parent-child relation for the following tables:
CREATE TABLE `pages` (
id INT NOT NULL AUTO_INCREMENT,
name VARCHAR(100) NULL,
PRIMARY KEY ( id )
)
CREATE TABLE `pageObjects` (
id INT NOT NULL AUTO_INCREMENT,
object TEXT NULL,
lastChanged TIMESTAMP on update CURRENT_TIMESTAMP NOT NULL,
fkPageId int NOT NULL,
PRIMARY KEY ( id )
)
The pages have a one:many relation with pageObjects.
Whenever the pageObjects records connected to a single page exceed 10, all records that are edited furthest in the past and exceeding the 10 must be deleted.
I wanted to do this in a single query, but I can't seem to figure this out...
This is how far I've gotten:
DELETE
FROM pageObjects
WHERE id NOT IN (
SELECT po.id, po.fkPageId FROM (
SELECT objects.fkPageId FROM (
SELECT COUNT(*) as count, fkPageId
FROM pageObjects
GROUP BY fkPageId
) objects
WHERE count > 10
) AS page
JOIN pageObjects po
ON page.fkPageId = po.fkPageId
AND po.lastChanged < (
SELECT MIN(lastChanged )
FROM pageObjects
WHERE fkPageId = po.fkPageId
GROUP BY fkPageId
ORDER BY lastChanged DESC
LIMIT 10
)
)
Sadly, the LIMIT bit in the bottom sub-query is not working the way I want to, because the MIN() function should be applied AFTER the LIMIT is applied.
So I tried that:
DELETE
FROM pageObjects
WHERE id NOT IN (
SELECT po.id, po.fkPageId FROM (
SELECT objects.fkPageId FROM (
SELECT COUNT(*) as count, fkPageId
FROM pageObjects
GROUP BY fkPageId
) objects
WHERE count > 10
) AS page
JOIN pageObjects po
ON page.fkPageId = po.fkPageId
AND po.lastChanged < (
SELECT MIN(lastChanged)
FROM (
SELECT lastChanged
FROM pageObjects
WHERE fkPageId = po.fkPageId
GROUP BY fkPageId
ORDER BY lastChanged DESC
LIMIT 10
)
)
)
But this is not possible, because the po.fkPageId is not available in the sub-query of the sub-query.
Is there any way to do this like this?
You can do this quite simply by counting the number of later entries for each id:
DELETE FROM pageObjects
WHERE id IN (
SELECT id FROM pageObjects po
WHERE (
SELECT count(id)
FROM pageObjects po2
WHERE po2.fkPageId = po.fkPageId
AND po2.lastChanged > po.lastChanged
) > 10
)
Check out what the select returns here:
http://www.sqlfiddle.com/#!9/f5218f/1/0

Add a subquery result to a query

I have to following subquery
SELECT
(
SELECT SUM( `total_amount` )
FROM `civicrm_contribution`
WHERE `currency` = 'PCT'
AND `contact_id` = ( SELECT `id`FROM `civicrm_contact`
WHERE `first_name` = ''
UNION ALL
SELECT `contact_id` FROM `civicrm_membership`
WHERE `source` = '')
)
-
(
SELECT SUM(`fee_amount`)
FROM `civicrm_participant`
WHERE `fee_level`='puncte'
AND `contact_id` = (SELECT `id` FROM `civicrm_contact`
WHERE `first_name` =''
UNION ALL
SELECT `contact_id` FROM `civicrm_membership`
WHERE `source` = '')
)As RemainingPoints
and i don't know how to add the result to following queries
SELECT `civicrm_contact`.first_name,`civicrm_membership`.start_date, `civicrm_membership`.end_date, `civicrm_membership`.source FROM `civicrm_contact`, `civicrm_membership` WHERE `civicrm_contact`.id = `civicrm_membership`.contact_id AND `civicrm_membership`.source =''
UNION ALL
SELECT `civicrm_contact`.first_name,`civicrm_membership`.start_date, `civicrm_membership`.end_date, `civicrm_membership`.source FROM `civicrm_contact`, `civicrm_membership` WHERE `civicrm_contact`.id = `civicrm_membership`.contact_id AND `civicrm_contact`.first_name ='test1'
I would like that the "RemainingPoints" to be added at the end of the query
first_name ;start_date; end_date; source; "RemainingPoints"
SELECT *
FROM (SELECT `civicrm_contact`.first_name,`civicrm_membership`.start_date, `civicrm_membership`.end_date, `civicrm_membership`.source FROM `civicrm_contact`, `civicrm_membership` WHERE `civicrm_contact`.id = `civicrm_membership`.contact_id AND `civicrm_membership`.source =''
UNION ALL
SELECT `civicrm_contact`.first_name,`civicrm_membership`.start_date, `civicrm_membership`.end_date, `civicrm_membership`.source FROM `civicrm_contact`, `civicrm_membership` WHERE `civicrm_contact`.id = `civicrm_membership`.contact_id AND `civicrm_contact`.first_name ='test1'
) x
JOIN (SELECT (
SELECT SUM( `total_amount` )
FROM `civicrm_contribution`
WHERE `currency` = 'PCT'
AND `contact_id` = ( SELECT `id`FROM `civicrm_contact`
WHERE `first_name` = ''
UNION ALL
SELECT `contact_id` FROM `civicrm_membership`
WHERE `source` = '')
)
-
(
SELECT SUM(`fee_amount`)
FROM `civicrm_participant`
WHERE `fee_level`='puncte'
AND `contact_id` = (SELECT `id` FROM `civicrm_contact`
WHERE `first_name` =''
UNION ALL
SELECT `contact_id` FROM `civicrm_membership`
WHERE `source` = '')
) As RemainingPoints) y

where clause while joining select

I need to get payment sum for all period and for current month. There are two tables: category and transactions.
transactions:
id int(11)
category varchar(32)
dttm_added datetime
minus float
minus_currency varchar(32)
categories:
id int(11)
key varchar(32)
name varchar(50)
type varchar(1)
Here is my query:
select `key`, `id`, `name`, minus_month,month_cur
from `categories` as ct
left join (
select `category` as tr_ct_m, date_format(`dttm_trans`, '%Y%m') as dat, sum(`minus`) as minus_month, `minus_currency` as month_cur from `transactions` where dat = date_format(now(), '%Y%m')
) as tr_m on tr_m.tr_ct_m = ct.key
where `type` = '-'
I need to check clause in select before joining, because after sum of minus_month all dates are turning to Null.
Help plz
not sure if i understand you correctly,please try below:
select `ct`.`key`, `ct`.`id`, `ct`.`name`, tr_m.minus_month,tr_m.month_cur from
( select * from `categories` where `type` = '-') as ct
left join
( select `category` as tr_ct_m, date_format(`dttm_trans`, '%Y%m') as dat,
sum(`minus`) as minus_month, `minus_currency` as month_cur from `transactions`
where dat = date_format(now(), '%Y%m')
) as tr_m on tr_m.tr_ct_m = ct.key

How do I combine these two Select queries with an OR case

I want to select all rows where WHERE (uid = {$uid} OR uid = **HERE** ) where **HERE** is the cids retreived from query 2 below.
Query 1:
SELECT * FROM `t_activities`
WHERE (`uid` = {$uid} OR `uid` = **HERE** )
AND `del` = 0
GROUP BY `fid`
ORDER BY `time` DESC
LIMIT 10
And Query 2:
SELECT `cid` FROM `t_con` WHERE `uid` = {$uid} AND `flag` = 1
SELECT * FROM `t_activities`
WHERE (`uid` = {$uid} OR `uid` in (SELECT `cid`
FROM `t_con`
WHERE `uid` = {$uid} AND `flag` = 1))
AND `del` = 0
GROUP BY `fid`
ORDER BY `time` DESC
LIMIT 10
You can do this as a join as well:
SELECT *
FROM `t_activities` ta left outer join
(SELECT `cid`
FROM `t_con`
WHERE `uid` = {$uid} AND `flag` = 1)
) tc
on ta = tc.cid
WHERE (`uid` = {$uid} OR tc.`uid` is not null) AND `del` = 0
GROUP BY `fid`
ORDER BY `time` DESC
LIMIT 10
By the way, as a SQL statement the "GROUP BY fid" looks very strange. This is allowed in mysql, but I think it is a bad practice. It is much better to be explicit about what you are doing:
SELECT fid, min(<field1>) as Field1, . . .
This helps prevent mistakes when you go back to the query or try to modify it.