Combined MySQL query help - 2 tables - mysql

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

Related

How to join table multiple times to get count of each genre in transaction

My tables are
TRANSACTION TABLE
transaction_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
customer_id INT,
inventory_id INT,
kiosk_id INT,
rental_out DATETIME,
rental_proposal INT,
rental_due DATETIME,
rental_cost FLOAT,
rental_in DATETIME,
rental_period INT,
rental_past_due INT,
late_fee INT
INVENTORY TABLE
inventory_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
title_id INT,
title_name VARCHAR(255),
genre_id INT,
genre_name VARCHAR(255),
qty INT
I'm trying to figure out a way in order to create a query that allows the inventory_id from the transaction table to create a count of the genre's of each transaction. My query allows to find the number of genres transacted, but only one at a time.
SELECT COUNT(genre_id)
FROM inventory
INNER JOIN transactions
ON inventory.title_id = transactions.inventory_id
WHERE transactions.customer_id = 1 and inventory.genre_id = 1;
I'd like to figure out a way to join the table multiple times to display the number of times each genre has been rented, being currently existing genres 1, 2, 3, 4, 5, 6
So far, I've come up these queries, but I don't see a logical way to the solution.
SELECT COUNT(A.genre_id) as GENRE_A, COUNT(A.genre_id) as GENRE_B, COUNT(A.genre_id) as GENRE_C FROM inventory A
INNER JOIN transactions D ON A.title_id = D.inventory_id
INNER JOIN transactions E ON A.title_id = E.inventory_id
INNER JOIN transactions F ON A.title_id = F.inventory_id
WHERE A.genre_id = 1 AND A.genre_id = 2 and A.genre_id = 3;
SELECT COUNT(A.inventory_id), COUNT(B.inventory_id), COUNT(C.inventory_id) FROM transactions A, transactions B, transactions C
INNER JOIN inventory D ON A.inventory_id = D.title_id
INNER JOIN inventory E ON A.inventory_id = E.title_id
INNER JOIN inventory F ON A.inventory_id = F.title_id
WHERE A.genre_id = 1 AND B.genre_id = 2 and C.genre_id = 3;
I've tried multiple variations, some of which I've deleted and haven't posted, but I can't seem to figure it out. Is there any solution? Any help would be greatly appreciated. Thank you!
Just use conditional aggregation.
Only 1 join needed.
This will count the transactions for the 3 genres
SELECT
-- trans.customer_id,
COUNT(CASE WHEN inv.genre_id = 1 THEN trans.transaction_id END) AS genre1,
COUNT(CASE WHEN inv.genre_id = 2 THEN trans.transaction_id END) AS genre2,
COUNT(CASE WHEN inv.genre_id = 3 THEN trans.transaction_id END) AS genre3
FROM transactions trans
JOIN inventory inv ON inv.inventory_id = trans.inventory_id
WHERE inv.genre_id IN (1, 2, 3)
-- GROUP BY trans.customer_id
Why not put the results on multiple rows?
SELECT genre_id, COUNT(*)
FROM inventory i INNER JOIN
transactions t
ON i.title_id = t.inventory_id
WHERE t.customer_id = 1 and i.genre_id IN (1, 2, 3)
GROUP BY genre_id;

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:

mysql SELECT EXISTS on multiple tables

Have tables: person,person_ip
Both tables have pid column as a primary key, in table person there is column state_id, in table person_ip there is column ip.
Want to discover if specified IP address is assigned to person with state_id is not equal to 2. But always got result 1, even if state_id is 0, 1 or 2. Always got 0 only if ip address is not listed at all. What am I doing wrong?
SELECT EXISTS (
SELECT person_ip.PID
FROM person_ip,person
WHERE person.PID=person_ip.PID
AND person.state_id NOT IN (2)
AND person_ip.ip='10.11.12.13'
)
this seems like a simple join.. unless i'm missing something
select person.*
from person
inner join person_ip
on person.pid = person_ip.pid
where person.state_id <> 2
and person_ip.ip_address = '10.0.0.1'
If you want to exclude the ip_address if it has been assigned to any user with state = 2, even if it has also been assigned to a user without state = 2, then try:
select max(i)
from (
select *
from (
select 1 as i
from dual
where not exists (
select 1
from person p
inner join person_ip pi
on p.pid = pi.pid
where p.state_id = 2
and pi.ip_address = '10.0.0.1'
)
) q
union
select 0
) qq
(dual is a system table that can be used as a sort of stub table)
here's a fiddle showing both versions
update after some actual sleep
Okay, so the above query is a little.. out there. Back in the real world, this one is probably more appropriate:
select count(case when p1.state_id = 2 then 1 end)
from person p1
inner join person_ip pi1
on p1.pid = pi1.pid
where pi1.ip_address = '10.0.0.1'
group by pi1.ip_address;
This will return 1 or more if your ip_address has been used by someone with a state_id of 2, and 0 if it has never been used by someone with a state_id of 2.
It will return nothing if the ip has never been used.
this fiddle has all three of the above queries.
SELECT IF(COUNT(*)>0,1,0)
FROM person
INNER JOIN person_ip
ON person.pid = person_ip.pid
AND person_ip.ip_address = '10.0.0.1'
WHERE person.state_id <> 2

Using JOIN and SUM returns unwanted null row when WHERE condition is not met

Please consider the following query:
Select all payments of a user and UNION the results with the user's invoices.
SELECT `id`,
`amount` AS `value`,
'PAYMENT' AS `transaction_type`
FROM `payment`
WHERE `user_id` = $user_id
UNION ALL
SELECT `i`.`id`,
(-1) * SUM(`ii`.`unit_price` * `ii`.`quantity`) AS `value`,
'INVOICE' AS `transaction_type`
FROM `invoice` `i`
JOIN `invoiceitem` `ii` ON `ii`.`invoice_id` = `i`.`id`
WHERE `user_id` = $user_id AND `type` = 'invoice'
The problem is that for users that have no payment and no invoice, an unwanted row is returned like this:
id | value | transaction_type
=================================
NULL | 0 | NULL
But for users that have some data, the result is completely expected.
IMPORTANT EDIT
After some more research, I got that the problem should be from the second subquery below:
SELECT i.id,
(-1) * SUM(ii.unit_price * ii.quantity) AS `value`,
'INVOICE' AS `trans_type`
FROM invoice i
JOIN invoiceitem ii ON ii.invoice_id = i.id
WHERE user_id = 4 AND type = 'invoice'
which returns the following:
id | value | transaction_type
=================================
NULL | NULL | INVOICE
Of course the user with user_id = 4 has not yet any invoice. But for another user that has some invoices, the result is OK.
This row is created by the aggregate function SUM. In order to prevent this, use a valid GROUP BY clause, probably GROUP BY user_id
It's impossible to say with any certainty without understanding the complete table descriptions, but based on the update to your question, you need to eliminate rows that have NULL values for the column i.id:
SELECT i.id
, (-1) * SUM(ii.unit_price * ii.quantity) AS `value`
, 'INVOICE' AS `trans_type`
FROM invoice i
JOIN invoiceitem ii
ON ii.invoice_id = i.id
WHERE user_id = 4
AND type = 'invoice'
AND i.id IS NOT NULL
I'm guessing that there is a logical defect in your data model or there might be some other column you should use. I can speculate that this invoice row could be a cancelled order, but it is clear that a row exists where the id column is null, which is why it appears in the result.
To avoid such nulls just use a LEFT JOIN instead of INNER JOIN, so, replace your following sql line:
JOIN `invoiceitem` `ii` ON `ii`.`invoice_id` = `i`.`id`
for this one:
LEFT OUTER JOIN `invoiceitem` `ii` ON `ii`.`invoice_id` = `i`.`id`

feeding result of one query into another

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;