I stated in the above comments that this seems to be a case of a supertype-subtype relationship but, since changing your database structure may be a decision that lies outside of the scope of this question, I am going to focus on offering a solution for your current situation.
Then, after a couple of edits, I decided to include some DDL
statements and a brief description of my assumptions about your database structure, hoping that in this way my answer and its involved queries will be more easily comprehended.
Understanding of the situation and hypothetical DDL
As I percive the scenario, type_a
and type_b
may be two different kinds of reading
or measurement
that you are collecting for each device.
This way, although I am not completely sure of the meaning of the type_a.type_a_id
and type_b.type_b_id
columns, I assume that they are some sort of sequential_number
or row_number
or record_identifier
in each respective table. Likewise, the columns type_a.stats_time
and type_b.stats_time
are the exact points in time when a device
posts some sort of reading
.
I have also assumed that type_a.device_id
and type_b.device_id
are FOREIGN KEYS that make reference to the device
table that, in turn, has the column device.device_id
being used as some kind of sequential_number
or row_number
or record_identifier
that you have defined as PRIMARY KEY.
Having described my understanding of the state of affairs, please note that I am not suggesting that this is the optimal structure (since, naturally, I am not familiar with the real scenario, it may lack normalization, integrity, etc.), I am just making some assumptions based on the data samples and queries that you have presented, in order to deliver a possible solution for your specific situation. So, here is the speculative DDL
:
CREATE TABLE device
(
device_id INT NOT NULL AUTO_INCREMENT,
name CHAR(30) NOT NULL,
type CHAR(1) NOT NULL,
PRIMARY KEY (device_id),
UNIQUE INDEX uix_name (name)
);
CREATE TABLE type_a
(
type_a_id INT NOT NULL AUTO_INCREMENT,
device_id INT NOT NULL ,
stats_time DATETIME NOT NULL,
`status` CHAR(10) NOT NULL,
noise INT NOT NULL,
PRIMARY KEY (type_a_id),
CONSTRAINT FOREIGN KEY fk_type_a_device (device_id)
REFERENCES device(device_id),
UNIQUE INDEX `uix_device_id_and_stats_time` (device_id, stats_time)
);
CREATE TABLE type_b
(
type_b_id INT NOT NULL AUTO_INCREMENT,
device_id INT NOT NULL,
stats_time DATETIME NOT NULL,
`status` CHAR(10) NOT NULL,
`signal` INT NOT NULL,
PRIMARY KEY (type_b_id),
CONSTRAINT FOREIGN KEY fk_type_b_device (device_id)
REFERENCES device(device_id),
UNIQUE INDEX `uix_device_id_and_stats_time` (device_id, stats_time)
);
-- Some ‘device’ INSERTS...
INSERT INTO device (name, type) VALUES ('First device', 'A');
INSERT INTO device (name, type) VALUES ('Second device', 'A');
-- ... And then, some ‘type_a’ and ‘type_b’ INSERTS
-- in order to have some sample data for retrieving.
INSERT INTO type_a (device_id, stats_time, `status`, noise)
VALUES (1, STR_TO_DATE('06/01/2015 08:10:01 AM', '%c/%e/%Y %r'), 'Foo', 123);
INSERT INTO type_a (device_id, stats_time, `status`, noise)
VALUES (1, STR_TO_DATE('04/04/2015 03:07:34 PM', '%c/%e/%Y %r'), 'Bar', 456);
INSERT INTO type_b (device_id, stats_time, `status`, `signal`)
VALUES (2, STR_TO_DATE('03/04/2015 02:08:15 PM', '%c/%e/%Y %r'), 'Boo', 789);
INSERT INTO type_b (device_id, stats_time, `status`, `signal`)
VALUES (2, STR_TO_DATE('05/07/2015 04:03:12 PM', '%c/%e/%Y %r'), 'Far', 852);
Initial proposal
And then, here is my first submited query which, while keeping the original idea, has been reformatted and adapted to the DDL
structure that was added later:
SELECT DE.device_id,
COALESCE(TA.type_a_id, TB.type_b_id) AS type_id,
DE.name,
DE.type,
COALESCE(TA.stats_time, TB.stats_time) AS stats_time,
COALESCE(TA.status, TB.status) AS `status`,
COALESCE(TA.noise, 0) AS noise,
COALESCE(TB.signal, 0) AS `signal`
FROM device DE
LEFT OUTER JOIN type_a TA
ON TA.device_id = DE.device_id
LEFT OUTER JOIN type_b TB
ON TB.device_id = DE.device_id
ORDER BY stats_time DESC;
As you can see, I am using the COALESCE() function that only if the column type_a.stats_time
contains a NULL
value, then ‘prints’ the value of the column type_b.stats_time
, and works in a similar way for the type_a.status
and type_b.status
columns.
Subsequent proposal
First method
Now, having reread your question and after some comment interactions, I understand that what you actually want is to obtain a single result set containing only two rows —one row including columns corresponding to the latest type_a.stats_time
value and one row holding columns related to the latest type_b.stats_time
value—. Therefore I suggest you the following query that also retrieves data from the DDL
proposed above:
(SELECT DE.device_id,
TA.type_a_id AS type_id,
DE.name,
DE.type,
TA.stats_time AS stats_time,
TA.status,
TA.noise,
NULL AS `signal`
FROM device DE
JOIN type_a TA
ON TA.device_id = DE.device_id
WHERE TA.stats_time = (SELECT MAX(stats_time)
FROM type_a))
UNION
(SELECT DE.device_id,
TB.type_b_id AS type_id,
DE.name,
DE.type,
TB.stats_time AS stats_time,
TB.status,
NULL AS noise,
TB.signal
FROM device DE
JOIN type_b TB
ON TB.device_id = DE.device_id
WHERE TB.stats_time = (SELECT MAX(stats_time)
FROM type_b))
ORDER BY stats_time DESC;
Note the use of the UNION operator, which in serves the purpose of combining in one single result set the latest row of type_a
(based on the newest stats_time
column value, obtained via the MAX() function within a subquery in the WHERE clause) with the latest row in type_b
(likewise, based on the newest stats_time
column value, also obtained through the MAX() function within a subquery in the WHERE clause).
Second method
You can also try with this alternate query, which sorts each one of the combined result sets based on the stats_time
column in each respective SELECT statement using the ORDER BY and LIMIT clauses.
(SELECT DE.device_id,
TA.type_a_id AS type_id,
DE.name,
DE.type,
TA.stats_time AS stats_time,
TA.status,
TA.noise,
NULL AS `signal`
FROM device DE
JOIN type_a TA
ON TA.device_id = DE.device_id
ORDER BY TA.stats_time DESC LIMIT 1)
UNION
(SELECT DE.device_id,
TB.type_b_id AS type_id,
DE.name,
DE.type,
TB.stats_time AS stats_time,
TB.status,
NULL AS noise,
TB.signal
FROM device DE
JOIN type_b TB
ON TB.device_id = DE.device_id
ORDER BY TB.stats_time DESC LIMIT 1)
ORDER BY stats_time DESC;
Once you compare the performance of all the suggested methods, it will be easy to define which one is the best for your needs. Also, if one of them solves your problem, then you can set it up as a VIEW
, this way future data retrieval will be easier to obtain.
About the speed performance aspect of your queries, you may start refining such matters by paying special attention to the indexes definition in the apropiate columns, e.g. type_a.stats_time
and type_b.stats_time
.