feeding result of one query into another - mysql

I tried to simplify my question to a basic example I wrote down below, the actual problem is much more complex so the below queries might not make much sense but the basic concepts are the same (data from one query as argument to another).
Query 1:
SELECT Ping.ID as PingID, Base.ID as BaseID FROM
(SELECT l.ID, mg.DateTime from list l
JOIN mygroup mg ON mg.ID = l.MyGroup
WHERE l.Type = "ping"
ORDER BY l.ID DESC
) Ping
INNER JOIN
(SELECT l.ID, mg.DateTime from list l
JOIN mygroup mg ON mg.ID = l.MyGroup
WHERE l.Type = "Base"
ORDER BY l.ID DESC
) Base
ON Base.DateTime < Ping.DateTime
GROUP BY Ping.ID
ORDER BY Ping.ID DESC;
+--------+--------+
| PingID | BaseID |
+--------+--------+
| 11 | 10 |
| 9 | 8 |
| 7 | 6 |
| 5 | 3 |
| 4 | 3 |
+--------+--------+
// from below I need to replace 11 by PingID above and 10 by BaseID above then the results to show up on as third column above (0 if no results, 1 if results)
Query 2:
SELECT * FROM
(SELECT sl.Data FROM list l
JOIN sublist sl ON sl.ParentID = l.ID
WHERE l.Type = "ping" AND l.ID = 11) Ping
INNER JOIN
(SELECT sl.Data FROM list l
JOIN sublist sl ON sl.ParentID = l.ID
WHERE l.Type = "base" AND l.ID = 10) Base
ON Base.Data < Ping.Data;
How can I do this? Again I'm not sure what kind of advice I will receive but please understand that the Query 2 is in reality over 200 lines and I basically can't touch it so I don't have so much flexibility as I'd like and ideally I'd like to get this working all in SQL without having to script this.
CREATE DATABASE lookback;
use lookback;
CREATE TABLE mygroup (
ID BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
DateTime DateTime
) ENGINE=InnoDB;
CREATE TABLE list (
ID BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
Type VARCHAR(255),
MyGroup BIGINT NOT NULL,
Data INT NOT NULL
) ENGINE=InnoDB;
CREATE TABLE sublist (
ID BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
ParentID BIGINT NOT NULL,
Data INT NOT NULL
) ENGINE=InnoDB;
INSERT INTO mygroup (DateTime) VALUES ("2012-03-09 22:33:19"), ("2012-03-09 22:34:19"), ("2012-03-09 22:35:19"), ("2012-03-09 22:36:19"), ("2012-03-09 22:37:19"), ("2012-03-09 22:38:19"), ("2012-03-09 22:39:19"), ("2012-03-09 22:40:19"), ("2012-03-09 22:41:19"), ("2012-03-09 22:42:19"), ("2012-03-09 22:43:19");
INSERT INTO list (Type, MyGroup, Data) VALUES ("ping", 1, 4), ("base", 2, 2), ("base", 3, 4), ("ping", 4, 7), ("ping", 5, 8), ("base", 6, 7), ("ping", 7, 8), ("base", 8, 3), ("ping", 9, 10), ("base", 10, 2), ("ping", 11, 3);
INSERT INTO sublist (ParentID, Data) VALUES (1, 2), (2, 3), (3, 6), (4, 8), (5, 4), (6, 5), (7, 1), (8, 9), (9, 11), (10, 4), (11, 6);

The simplest way of dealing with this is temporary tables, described here and here. If you create an empty table to store your results (let's call it tbl_temp1) you can to this:
INSERT INTO tbl_temp1 (PingID, BaseID)
SELECT Ping.ID as PingID, Base.ID as BaseID
FROM ...
Then you can query it however you like:
SELECT PingID, BaseID from tbl_temp1 ...
Edited to add:
From the docs for CREATE TEMPORARY TABLE:
You can use the TEMPORARY keyword when creating a table. A TEMPORARY
table is visible only to the current connection, and is dropped
automatically when the connection is closed. This means that two
different connections can use the same temporary table name without
conflicting with each other or with an existing non-TEMPORARY table of
the same name. (The existing table is hidden until the temporary table
is dropped.)

If this were a more flattened query, then there would a straightforward answer.
It is certainly possible to use a derived table as the input to outer queries. A simple example would be:
select
data1,
(select data3 from howdy1 where howdy1.data1 = greetings.data1) data3_derived
from
(select data1 from hello1 where hello1.data2 < 4) as greetings;
where the derived table greetings is used in the inline query. (SQL Fiddle for this simplistic example: http://sqlfiddle.com/#!3/49425/2 )
Following this logic would lead us to assume that you could cast your first query as a derived table of query1 and then recast query2 into the select statement.
For that I constructed the following:
select query1.pingId, query1.baseId,
(SELECT ping.Data pingData FROM
(SELECT sl.Data FROM list l
JOIN sublist sl ON sl.ParentID = l.ID
WHERE l.Type = "ping" AND l.ID = query1.pingId
) Ping
INNER JOIN
(SELECT sl.Data FROM list l
JOIN sublist sl ON sl.ParentID = l.ID
WHERE l.Type = "base" AND l.ID = query1.baseId
) Base
ON Base.Data < Ping.Data)
from
(SELECT Ping.ID as PingID, Base.ID as BaseID FROM
(SELECT l.ID, mg.DateTime from list l
JOIN mygroup mg ON mg.ID = l.MyGroup
WHERE l.Type = "ping"
ORDER BY l.ID DESC
) Ping
INNER JOIN
(SELECT l.ID, mg.DateTime from list l
JOIN mygroup mg ON mg.ID = l.MyGroup
WHERE l.Type = "Base"
ORDER BY l.ID DESC
) Base
ON Base.DateTime < Ping.DateTime
GROUP BY Ping.ID
) query1
order by pingId desc;
where I have inserted query2 into a select clause from query1 and inserted query1.pingId and query1.baseId in place of 11 and 10, respectively. If 11 and 10 are left in place, this query works (but obviously only generates the same data for each row).
But when this is executed, I'm given an error: Unknown column 'query1.pingId'. Obviously, query1 cannot be seen inside the nested derived tables.
Since, in general, this type of query is possible, when the nesting is only 1 level deep (as per my greeting example at the top), there must be logical restrictions as to why this level of nesting isn't possible. (Time to pull out the database theory book...)
If I were faced with this, I'd rewrite and flatten the queries to get the real data that I wanted. And eliminate a couple things including that really nasty group by that is used in query1 to get the max baseId for a given pingId.
You say that's not possible, due to external constraints. So, this is, ultimately, a non-answer answer. Not very useful, but maybe it'll be worth something.
(SQL Fiddle for all this: http://sqlfiddle.com/#!2/bac74/35 )

If you cannot modify query 2 then there is nothing we can suggest. Here is a combination of your two queries with a reduced level of nesting. I suspect this would be slow with a large dataset -
SELECT tmp1.PingID, tmp1.BaseID, IF(slb.Data, 1, 0) AS third_col
FROM (
SELECT lp.ID AS PingID, MAX(lb.ID) AS BaseID
FROM MyGroup mgp
INNER JOIN MyGroup mgb
ON mgb.DateTime < mgp.DateTime
INNER JOIN list lp
ON mgp.ID = lp.MyGroup
AND lp.Type = 'ping'
INNER JOIN list lb
ON mgb.ID = lb.MyGroup
AND lb.Type = 'base'
GROUP BY lp.ID DESC
) AS tmp1
LEFT JOIN sublist slp
ON tmp1.PingID = slp.ParentID
LEFT JOIN sublist slb
ON tmp1.BaseID = slb.ParentID
AND slb.Data < slp.Data;

Related

How to find NOT NULL columns by union?

I have three table as:
1.table person
id, others
1, x
2, y
3, z
4, w
2.table followup, FOREIGN KEY (person_id) REFERENCES person (id)
id, person_id, ftime, details
1, 1, '2018-01-12', 'json_1'
2, 1, '2018-04-21', 'json_2'
3, 2, '2017-12-16', 'json_3'
4, 2, '2018-01-17', NULL
5, 3, '2018-06-02', 'json_5'
6, 4, '2018-01-19', NULL
3.table followup_track, FOREIGN KEY (fid) REFERENCES followup (id)
id, fid, ftime, details
1, 1, '2018-01-27', 't_json_1'
2, 2, '2018-05-07', 't_json_2'
3, 5, '2018-06-17', 't_json_3'
Now I want find all the last ftime of every person, and the details IS NOT NULL in followup and/or followup_track.
the result what I want to get is (here the pid is person's id):
pid, ftime, details
1, '2018-05-07', 't_json_2'
2, '2017-12-16', 'json_3'
3, '2018-06-17', 't_json_3'
Because there is no detalis != NULL for person.id = 4, so the result no need for pid=4.
Because the last time of person.id = 1 is '2018-05-07', so need that column.
I create a view like:
CREATE VIEW view_full_flup AS
SELECT
p.id AS pid, fp.ftime, fp.details
FROM
((followup_track fp
LEFT JOIN followup ON (fp.fid = followup.id))
LEFT JOIN person p ON (followup.person_id = p.id))
WHERE
fp.details IS NOT NULL
UNION
SELECT
f.person_id AS pid, f.ftime, f.details
FROM
followup f
WHERE
f.details IS NOT NULL
Then, I use sql:
SELECT *, MAX(`ftime`) FROM view_full_flup GROUP BY pid;
Is my solution right please? The details can not make index, and it is slow. How to do this right please?
You can’t select star group by(well, you might be able to in MySQL depending on how it’s set up but it’s not a great habit to get into for portability of sql skills), you have to specify a list of columns in your select and either group them (put the column name in the group by) or aggregate them (pass the column name into an aggregate function)
It feels like you’ve got your left join logic backwards- person is the only table you know you have records in, so it should be on the left side of the join.. the other tables are potentially recordless
We’re this my query I’d write it something more like:
select
Pid,
Max(case when ft.ftime > f.ftime then ft.ftime else f.ftime end) maxft
From
Person p
Left join (select * from followup where details is not null) f on f.person_id = p.id
Left join (select * from follup_track where details is not null) ft on ft.fid = f.id
Group by pid
We just join the set of tables once, having already filtered for records where either table’s details are present and then get the max date from either table
Note that this query can return null dates, if you have records where the date column is null even though details is filled in. If those are undesirable, filter hem with a HAVING or wrap the whole thing in another select and filter using a WHERE
Ps; as it stands, this query doesn’t seem particularly useful because though you know the most recent date you don’t have any other data. If it’s all you wanted to know (I wasn’t able to tell from your original post because the description and the query disagreed in what they said they wanted) then great, but if you wanted the other info, maybe using mysql 8’s new analytic queries would better suit:
Select * from
(
select
Pid,
Row_number over(partition by pid order by (case when ft.ftime > f.ftime then ft.ftime else f.ftime end) desc) rown
From
Person p
Left join (select * from followup where details is not null) f on f.person_id = p.id
Left join (select * from follup_track where details is not null) ft on ft.fid = f.id
) a where rown = 1

SQL query to get children of entity into same row as parent entity

I have a table that has the following fields:
| entity_id | parent_entity_id | name | status |
|----------------------------------------------|
I'm attempting to write a query that displays every entity that doesn't have a parent and displays their children's name and status inline for a result like this:
| entity_id | name | child_entity_1_name | child_entity_1_status |...| child_entity_4_name | child_entity_4_status |
--------------------------------------------------------------------------------------------------------------------
I know the data is structured so that every entity has at least 3 children, but not every entity has 4 (therefore the ones with 3 will have NULL in the columns for the 4th child name and status). Furthermore, I know that no entity that has a parent is a parent itself.
From the introductory database classes I've taken, this seems like a complicated query. The part that's tripping me up is getting all of the sub-entities into the same row. I can get one sub-entity in the same row as its parent but can't get more than one.
EDIT: The database is basically a set of trees each with a height of 2. There are no grandparents.
PARENT_ENT_1 PARENT_ENT_2
| | | | | | | |
| | | | | | | |
C1 C2 C3 C4 C5 C6 C7 C8
Every row in my result query should represent one of these trees
This works: http://sqlfiddle.com/#!9/e1127f/27/0
But I feel like it should be much, much easier.
I basically had this:
SELECT P.entity_id as Parent_id, P.name as Parent_Name, C1.entity_id, C1.Name,
C2.entity_id, C2.Name, C3.entity_id, C4.Name, C4.entity_id, C4.Name
FROM entity P
JOIN entity C1 on C1.parent_entity_id = P.entity_id
JOIN entity C2 on C2.parent_entity_id = P.entity_id
JOIN entity C3 on C3.parent_entity_id = P.entity_id
LEFT JOIN entity C4 on C4.parent_entity_id = P.entity_id
WHERE P.parent_entity_id IS NULL
AND C1.entity_id < C2.entity_id
AND C2.entity_id < C3.entity_id
AND C3.entity_id < C4.entity_id
But of course that final join won't work as it is there, because the WHERE clause turns it into an INNER join.. Maybe someone will see an easy way to handle that part.
I ended up relenting and using a UNION, one half for parents with 3 children and the other for parents with 4.
Edit: Thank you Paul for making the final join work!
SELECT P.entity_id as Parent_id, P.name as Parent_Name, C1.entity_id, c1.Name,
C2.entity_id, c2.Name, C3.entity_id, c3.Name, C4.entity_id, c4.Name
FROM entity P
JOIN entity C1 on C1.parent_entity_id = P.entity_id
JOIN entity C2 on C2.parent_entity_id = P.entity_id
JOIN entity C3 on C3.parent_entity_id = P.entity_id
LEFT JOIN entity C4 on C4.parent_entity_id = P.entity_id
and c3.entity_id < c4.entity_id
WHERE p.parent_entity_id IS NULL
AND C1.entity_id < C2.entity_id
AND C2.entity_id < C3.entity_id
AND (3 = (SELECT COUNT(1)
FROM entity c
WHERE c.parent_entity_id = p.entity_id)
OR c4.entity_id is not null)
Here is a query for two children:
select
p.entity_id, p.name,
c1.name as child_entity_1_name,
c1.status as child_entity_1_status,
c2.name as child_entity_2_name,
c2.status as child_entity_2_status
from entities p
left join entities c1 on c1.entity_id = (
select c.entity_id
from entities c
where c.parent_entity_id = p.entity_id
order by c.entity_id asc
limit 1
offset 0
)
left join entities c2 on c2.entity_id = (
select c.entity_id
from entities c
where c.parent_entity_id = p.entity_id
order by c.entity_id asc
limit 1
offset 1
)
where p.parent_entity_id is null
For child_entity_3 you will use offset 2 and for child_entity_4 you will use offset 3.
But I would rather just use the following two queries
select p.entity_id, p.name
from entities p
where p.parent_entity_id is null;
select p.entity_id as parent_id, c.name, c.status
from entities p
join entities c on c.parent_entity_id = p.entity_id
where p.parent_entity_id is null
order by p.entity_id, c.entity_id;
and create the desired table in application language with a couple of simple loops.
SET #cNum := 0;
SET #prevParent := 0;
SELECT p.id, p.Name
, GROUP_CONCAT(IF(numberedChildren.childNum = 1, c.Name, NULL)) AS child_entity_1_name
, GROUP_CONCAT(IF(numberedChildren.childNum = 1, c.Status, NULL)) AS child_entity_1_status
, GROUP_CONCAT(IF(numberedChildren.childNum = 2, c.Name, NULL)) AS child_entity_2_name
, GROUP_CONCAT(IF(numberedChildren.childNum = 2, c.Status, NULL)) AS child_entity_2_status
, ...
FROM (
SELECT #cNum := IF(#prevParent <> orderedChildren.parent_id, #cNum + 1, 1) AS childNum
, orderedChildren.id AS child_id
, #prevParent := orderedChildren.parent_id AS parent_id
FROM (
SELECT parent_id, id
FROM sometable
ORDER BY parent_id, id
) AS orderedChildren
) AS numberedChildren
INNER JOIN sometable AS p ON numberedChildren.parent_id = p.id
INNER JOIN sometable AS c ON numberedChildren.child_id = c.id
GROUP BY p.id, p.Name
;
I think this script might work. It relies on GROUP_CONCAT, and pretty much any other aggregate function, ignoring null values.
You can probably also make it a single query (dropping the initial SET statements) by changing this line:
) AS orderedChildren
to
) AS orderedChildren, (SELECT #cNum AS cnInit, #prevParent AS ppInit) As init
but that is not my usual style for session variable init.
Edit: Also, ordered children may not NEED to be a subquery (you might be able to do the ORDER BY and childNum calculation in the same subquery) but such use of session variables can be...delicate.
This is a bit messy, and there's probably a better means of storing this, especially because this is only manually scalable.
Assuming a table:
CREATE TABLE
parents
(
entity_id INT PRIMARY KEY AUTO_INCREMENT,
parent_entity_id INT,
name VARCHAR(15),
`status` VARCHAR(15)
);
EDITED
With some sample data:
INSERT INTO
`parents`
(entity_id, parent_entity_id, name, `status`)
VALUES
(1, NULL, 'Parent1', 'sfsd'),
(2, 1, 'Child1A', 'sfsd'),
(3, 1, 'Child1B', 'sfsd'),
(4, 1, 'Child1C', 'sfsd'),
(5, NULL, 'Parent2', 'sfsd'),
(6, 5, 'Child2A', 'sfsd'),
(7, 5, 'Child2B', 'sfsd');
You can create a view, temporary table or permanent table (depending on your ultimate goal) that stores the following:
SET #row_number = 0;
SET #parent_id = 0;
SELECT
#row_number:=CASE
WHEN #parent_id = parent_entity_id THEN #row_number + 1
ELSE 1
END AS `child_num`,
entity_id,
#parent_id:= parent_entity_id as parent_entity_id,
name,
`status`
FROM
`parents`
WHERE
`parent_entity_id` IS NOT NULL
ORDER BY
parent_entity_id ASC,
entity_id ASC;
The above would be easier with SQL Server and using PARTITION BY and ROW_NUMBER, but this is a way around it.
Gives us:
Then, you could join that table/view 3 times, adding a second JOIN condition for the child number. This is demoed here, using a derived table due to the restrictions in SQL Fiddle with data modification, which I supposed could be done all 3 times, though you'd have to look into the efficiency and benchmarking.
http://sqlfiddle.com/#!9/5ddef3/3
Ultimately, it gives us:

WHERE (set) IN (set)

EDIT: I seemed to have asked this question incorrectly.
I'm trying to find a way to query if a set is available in another set. For example:
SELECT * FROM something
WHERE (1, 3) IN (1, 2, 3, 4, 5)
In this case, 1 & 3 are in the set (1, 2, 3, 4, 5). Another example:
SELECT * FROM something
WHERE (1, 3) IN (1, 5, 7, 9);
In this case, 1 & 3 ARE NOT in the set (1, 5, 7, 9) so nothing should be pulled from the table.
NOTE: This answers the original question, which seems to have nothing to do with the question after OP modifications.
You can get the users who completed all three levels by using:
SELECT cl.user_id
FROM completed_levels cl
WHERE cl.id IN (3, 5, 7)
GROUP BY cl.user_id
HAVING COUNT(DISTINCT cl.id) = 3;
(Note: DISTINCT is not necessary if the ids for a given user are unique.)
THEN, you can get what you want using a JOIN or similar construct:
SELECT u.*
FROM users u JOIN
(SELECT cl.user_id
FROM completed_levels cl
WHERE cl.id IN (3, 5, 7)
GROUP BY cl.user_id
HAVING COUNT(DISTINCT cl.id) = 3
) cu
ON cl.user_id = u.id;
NEW REQUEST (according to sqlfiddle.com/#!9/f36d92/2):
# The goal is to write a query that will select all exercises
# that the user has the correct equipment for, where the pre-defined
# set is the id's of the equipment the user has.
# For example, let's assume the user has equipment (1, 4)
# The exercise "Curls" should be pulled from the table, as the user has all
# of the required equipment based on the exercise_requirements table.
# while "Wrecking Ball" is not returned as the user only has a portion of the
# required equipment.
# If the user's equipment was (1, 3, 4) then both "Curls" and "Wrecking ball"
# would be returned from the exercises table, as the user has the required equipment
# for both exercises.
#----
#Below is my take on your query.
SELECT ex.* FROM exercises ex
WHERE ex.id IN (
SELECT exercise_id FROM exercise_requirements
WHERE ex.id IN (1, 4)
GROUP BY exercise_id
HAVING COUNT(distinct exercise_id) = 3
);
SOLUTION:
You are confusing some IDs here. This would be closer:
SELECT ex.* FROM exercises ex
WHERE ex.id IN (
SELECT exercise_id FROM exercise_requirements
WHERE equipment_id IN (1, 4)
GROUP BY exercise_id
HAVING COUNT(distinct equipment_id) = 2
);
But still this query is vice versa. We don't want to know whether all the user's equipment are found in a set of equipment needed for an exercise, but whether the whole set of equipment needed for an exercise is found in the user's equipment.
Probably the easiest way to write this is: aggregate exercise_requirements per exercise_id and check that no equipment_id is needed that the user doesn't have.
select *
from exercises
where id in
(
select exercise_id
from exercise_requirements
group by exercise_id
having sum(equipment_id not in (1, 4)) = 0
);
Your updated fiddle: http://sqlfiddle.com/#!9/f36d92/5
You are using the IN clause with a correlated subquery (i.e. the subquery references u.id). This is not how we use it. The IN clause is great for non-correlated subqueries; if you need a correlated subquery, use EXISTS instead. For your problem a non-correlated subquery suffices, so use IN accordingly:
select *
from users
where u.id in (select user_id from completed_levels where id in (1, 5, 7);
If a user must have all levels:
select *
from users
where u.id in (select user_id from completed_levels where id = 1
and u.id in (select user_id from completed_levels where id = 5
and u.id in (select user_id from completed_levels where id = 7;
Such problems are usually better solved with an aggregation so as not to have to query the same table again and again:
select *
from users
where u.id in
(
select user_id
from completed_levels where id in (1, 5, 7)
group by user_id
having count(distinct id) = 3
);
You could use this
SELECT u.*
FROM users u
INNER JOIN completed_levels cl
ON cl.user_id = u.id
WHERE cl.id IN (1, 5, 7);
Or using EXISTS as link from #DanFromGermany
You can use Case to make a Sum which will increase with 1 for each level within 1, 5 & 7.
SELECT A.* FROM users AS
INNER JOIN
(
SELECT U.id,
SUM(CASE WHEN
(
A.completed_levels = 1
OR A.completed_levels = 5
OR A.completed_levels = 7
) THEN 1 ELSE 0 END
) AS RN
FROM completed_levels A
INNER JOIN users U ON A.user_id = U.id
GROUP BY U.id
) B ON A.id = B.id
WHERE B.RN = 3 -- Those users have completed level 1, 5 & 7 will have RN = 3 only

Combined MySQL query help - 2 tables

I have a table rosters and a table members. They are setup like this:
rosters table
id team member
1 1 1
2 1 2
3 1 3
members table
id name last_active
1 Dan 1454815000
2 Ewan 1454817500
3 Cate 1454818369
I need to fetch all rows in rosters where team=1. Then, I need to take all those returned results and use the member column to fetch the largest last_active value from members where id is in that list of results returned from rosters
This is how I would do it in PHP, but I'm sure there's a way to just use a more efficient query.
$rosterList = $db->query('SELECT * FROM rosters WHERE team=1');
$lastActive = 0;
foreach($rosterList as $roster) {
$activity = $db->query('SELECT last_active FROM members WHERE id='.$roster['team']);
if ( $activity > $lastActive )
$lastActive = $activity;
}
if ( $lastActive > time()-60 )
echo 'team is currently online';
It would be nice if it could just return one result with the latest last_active column but if it returns all matches in the members table that would be fine too.
By using an ORDER BY (descending) on that last_actie column, then limiting to just 1 row, you get access to the whole member row.
sqlfiddle demo
MySQL 5.6 Schema Setup:
CREATE TABLE members
(`id` int, `name` varchar(4), `last_active` int)
;
INSERT INTO members
(`id`, `name`, `last_active`)
VALUES
(1, 'Dan', 1454815000),
(2, 'Ewan', 1454817500),
(3, 'Cate', 1454818369)
;
CREATE TABLE rosters
(`id` int, `team` int, `member` int)
;
INSERT INTO rosters
(`id`, `team`, `member`)
VALUES
(1, 1, 1),
(2, 1, 2),
(3, 1, 3)
;
Query 1:
select
m.*
from members m
join rosters r on m.id = r.member
where r.team = 1
order by m.last_active DESC
limit 1
Results:
| id | name | last_active |
|----|------|-------------|
| 3 | Cate | 1454818369 |
You can use following solution:
$result_array = mysql_query("SELECT * FROM rosters as r INNER JOIN members as m on r.member=m.id AND r.team = '1' ORDER BY last_active DESC LIMIT 1");
$lastActive = 0;
if($result_array)
{
while($row = mysql_fetch_row($result_array,MYSQL_ASSOC))
{
$lastActive = $result_array['last_active'];
}
}
if ( !empty($lastActive) && $lastActive > time()-60 )
echo 'team is currently online';
I'm a little confused why you are retrieving rows from members where the id value matches the team value from rosters. Seems like you would want to match on the member column.
You can use a join operation and an aggregate function. To get the largest value of last_active for all members of a given team, using the member column, something like this:
SELECT MAX(m.last_active) AS last_active
FROM members m
JOIN rosters r
ON r.member = m.id
WHERE r.team = 1
To do the equivalent of the original example, using the team column (and again, I don't understand why you would do this, because it doesn't look right):
SELECT MAX(m.last_active) AS last_active
FROM members m
JOIN rosters r
ON r.team = m.id
WHERE r.team = 1
The MAX() aggregate function in the SELECT list, with no GROUP BY clause, causes all of the rows returned to be "collapsed" into a single row. The largest value of last_active from the rows that satisfy the predicates will be returned.
You can see how the join operation works by eliminating the MAX() aggregate...
SELECT m.last_active
, m.id AS member_id
, m.name AS member_name
, r.member
, r.team
, r.id AS roster_id
FROM members m
JOIN rosters r
ON r.member = m.id
WHERE r.team = 1
ORDER BY m.last_active DESC

Left outer join with multiple conditions - MYSQL

I'm running into some problems with a very simple query. I figure that it must be because of an incorrect assumption about how SQL queries work.
I'm trying to write a simple LEFT OUTER JOIN query using the following tables:
tmtrip_viewer( tmtrip_id, tmuser_id ) -> FKs: (tmtrip.id, tmuser.id)
Values: ( 6, 2 )
( 6, 3 )
( 7, 4 )
tmtrip( id, ...)
Values: (1, ...)
(2, ...)
(3, ...)
(4, ...)
tmuser(id, username, ...)
Values: (1, user1)
(2, user2)
(3, user3)
(4, user4)
What I want to do is:
Display alls id from tmuser table given the following conditions:
- That the id != '1'
- That the id is NOT in table tmtrip_viewer where tmtrip_viewer.tmtrip_id = 7.
Basically, I want to get all the users that are not viewing the tmtrip with tmtrip_id = 7 (except the logged in user ..id='1').
I have formulated the following query, but it does not behave as desired:
SELECT a.`id`, a.`username` FROM
`tmuser` a LEFT OUTER JOIN `tmtrip_viewer` b
ON a.`id` = b.`tmuser_id` AND b.`tmtrip_id` = '7'
WHERE a.id <> '1'
Why is this not working? What would be the right way to do this?
Add AND b.tmtrip_id IS NULL to your WHERE. Your query is getting all tmusers and their "trip 7" info if they have any; this will reduce the results to only the ones that had no "trip 7" info.
I think this should do what you want.
It would show one record for each user that doesn't have ID = 1 and also doesn't have a record in tm_tripviewer with tmtrip_id = 7.
SELECT id, username
FROM tmuser
WHERE id != 1
AND id NOT IN
(SELECT id FROM tmtrip_viewer WHERE tmtrip_id = 7)