I have a table for definition - def
Id Device Location
1 GGHY199 USA
12 DFGHY71 India
145 APPHY75 USA
And its transactions are recorded in a diff table -event
eventid deviceid event date
123 12 Login 12-01-2019
32 12 Unreachable 18-02-2019
223 145 Unreachable 19-02-2019
334 1 DOWN 01-03-2019
I want an output as
for every day, all three devices should show, if it has no transacion, it should show as null, with what i assume is the first date of the month in the date column.
like,
eventid deviceid event date
null 1 null 01-01-2019
123 12 Login 12-01-2019
null 145 null 01-01-2019
null 1 null 01-02-2019
32 12 Unreachable 18-02-2019
223 145 Unreachable 19-02-2019
334 1 DOWN 01-03-2019
null 12 null 01-03-2019
null 145 null 01-03-2019
currently im doing:
select * from def
left join
event on def.id=event.deviceid
and im obviously not getting what i want.
Thanks!
Assuming you have a valid calendar table for all the date you need (mimic by the select union all table t) you could try using left join between calendare and the others table
select t.date, e.eventid, e.device, e.event, d.device
from (
select '2019-01-01' date
union all
select '2019-01-02'
union all
select '2019-01-03'
.....
union all
select '2019-03-31'
) t
left event e on t.date = e.event
left join device d on e.device = d.id
You seem to be after something like this...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(event_id SERIAL PRIMARY KEY
,device_id INT NOT NULL
,event VARCHAR(12) NOT NULL
,date DATE NOT NULL
);
INSERT INTO my_table VALUES
(123, 12,'Login','2019-01-12'),
( 32, 12,'Unreachable','2019-02-18'),
(223,145,'Unreachable','2019-02-19'),
(334, 1,'DOWN','2019-03-01');
SELECT DISTINCT z.event_id
, x.device_id
, z.event
, y.date
FROM my_table x
JOIN my_table y
LEFT
JOIN my_table z
ON z.device_id = x.device_id
AND z.date = y.date
ORDER
BY date
, device_id;
+----------+-----------+-------------+------------+
| event_id | device_id | event | date |
+----------+-----------+-------------+------------+
| NULL | 1 | NULL | 2019-01-12 |
| 123 | 12 | Login | 2019-01-12 |
| NULL | 145 | NULL | 2019-01-12 |
| NULL | 1 | NULL | 2019-02-18 |
| 32 | 12 | Unreachable | 2019-02-18 |
| NULL | 145 | NULL | 2019-02-18 |
| NULL | 1 | NULL | 2019-02-19 |
| NULL | 12 | NULL | 2019-02-19 |
| 223 | 145 | Unreachable | 2019-02-19 |
| 334 | 1 | DOWN | 2019-03-01 |
| NULL | 12 | NULL | 2019-03-01 |
| NULL | 145 | NULL | 2019-03-01 |
+----------+-----------+-------------+------------+
Using CTE table, we can create dates and then join them with event table.
with t0 (i) as (select 0 union all select 0 union all select 0),
t1 (i) as (select a.i from t0 a ,t0 b ),
t2 (i) as (select a.i from t1 a ,t1 b ),
t3 (srno) as (select row_number()over(order by a.i) from t2 a ,t2 b ),
tbldt(dt) as (select dateadd(day,t3.srno-1,'01/01/2019') from t3)
select eventid, deviceid, event, tbldt.dt
from tbldt
left join event e on e.date = tbldt.dt
left join def d on e.deviceid = d.id
where tbldt.dt <= (select max(date) from event)
Related
Recently, I got a table A like this:
+----------+-----------+---------------+
| VoucherID| AccountID | TransactionID |
+----------+-----------+---------------+
| 48 | 96 | 10 |
+----------+-----------+---------------+
| 48 | 14 | 10 |
+----------+-----------+---------------+
| 37 | 14 | 88 |
+----------+-----------+---------------+
| 37 | 25 | 88 |
+----------+-----------+---------------+
| 57 | 12 | 30 |
+----------+-----------+---------------+
|.......
I try to SELF JOIN this table by
SELECT account_log.*, reciprocal_account.AccountID as Reciprocal_accountID
FROM A AS account_log
LEFT JOIN A AS reciprocal_account ON account_log.VoucherID = reciprocal_account.VoucherID
WHERE account_log.AccountID <> reciprocal_account.AccountID
I got this
+----------+-----------+---------------+----------------------+
| VoucherID| AccountID | TransactionID | Reciprocal_accountID |
+----------+-----------+---------------+----------------------+
| 48 | 96 | 10 | 14 |
+----------+-----------+---------------+----------------------+
| 37 | 14 | 88 | 25 |
+----------+-----------+---------------+----------------------+
What I expect is when VoucherID doesn't have any reciprocal account, that record will return null, like this
+----------+-----------+---------------+----------------------+
| VoucherID| AccountID | TransactionID | Reciprocal_accountID |
+----------+-----------+---------------+----------------------+
| 48 | 96 | 10 | 14 |
+----------+-----------+---------------+----------------------+
| 37 | 14 | 88 | 25 |
+----------+-----------+---------------+----------------------+
| 57 | 12 | 30 | null |
+----------+-----------+---------------+----------------------+
Is there any elegant approach? Thanks all.
Why not just use aggregation?
select transaction_id, voucher_id,
min(account_id) as account_id,
nullif(max(account_id), min(account_id)) as reciprocal_account_id
from a
group by transaction_id, voucher_id;
You could also do this using left join:
SELECT account_log.*, reciprocal_account.AccountID as Reciprocal_accountID
FROM A account_log LEFT JOIN
A reciprocal_account
ON account_log.VoucherID = reciprocal_account.VoucherID AND
account_log.AccountID < reciprocal_account.AccountID;
By including account_log.AccountID <> reciprocal_account.AccountID in your where clause, you are effectively changing the LEFT JOIN to an INNER JOIN, since <> will always be false if one argument is NULL. Add that condition to the ON clause instead, or use null-safe equals with a NOT: NOT(account_log.AccountID <=> reciprocal_account.AccountID)
If there are only ever a maximum of 2 transactions per voucher and you have an auto_increment id then you can use rownumber (if on version 8 or above) or correlated sub queries (any version)
DROP TABLE IF EXISTS t;
CREATE TABLE t
(id int auto_increment primary key,VoucherID INT, AccountID INT, TransactionID INT);
INSERT INTO T VALUES
(null, 48 , 96 , 10 ),
(null, 48 , 14 , 10 ),
(null, 37 , 14 , 88 ),
(null, 37 , 25 , 88 ),
(null, 57 , 12 , 30 );
select a.voucherid,a.accountid,a.transactionid,b.accountid
from
(
SELECT *, row_number() over (partition by voucherid order by id) rn
FROM t AS account_log
) a
left join
(SELECT *, row_number() over (partition by voucherid order by id) rn
FROM t AS account_log) b
on a.voucherid = b.voucherid and b.rn = a.rn+1
where a.rn = 1;
select t.voucherid,t.accountid,t.transactionid,
(Select accountid
from t t1
where t1.voucherid = t.voucherid and
t1.id > t.id
order by t1.id desc limit 1) raccount
from t
where id = (select min(id) from t t1 where t1.voucherid = t.voucherid)
;
Note the test to select the earliest id for the result
Both queries give the same result.
I might oversee something, but why not simply selecting the null entries as well at the left join?
SELECT account_log.*, reciprocal_account.AccountID as Reciprocal_accountID
FROM A AS account_log
LEFT JOIN A AS reciprocal_account ON account_log.VoucherID = reciprocal_account.VoucherID
WHERE account_log.AccountID <> reciprocal_account.AccountID OR reciprocal_account.AccountID is null
I ask for your understanding because I can not speak English well.
There are 4 tables.
sensor (PK: sensor)
sensor | service
1 | 1
2 | 2
equipment (PK: id, FK: sensor)
id | equip | sensor |
1 | 8 | 1 |
2 | 8 | 1 |
3 | 8 | 2 |
4 | 7 | 2 |
A (PK: AUTO INCREMENT, UNIQUE: id, time, FK: id)
id | time | temperature
1 | 1027 | 30
1 | 1028 | 30
1 | 1029 | NULL
1 | 1030 | 60
1 | 1101 | 999
B (PK: AUTO INCREMENT, UNIQUE: id, time, FK: id)
id | time | temperature
2 | 1027 | 40
2 | 1029 | 50
2 | 1030 | NULL
2 | 1031 | 59
I want the following results.
time | A_temperature | B_temperature
1027 | 30 | 40
1028 | 30 | NULL
1029 | NULL | 50
1030 | 60 | NULL
1031 | NULL | 59
So I made the following query:
SELECT DISTINCT COALESCE(A.time, B.time) time, A.temperature AS A_temperature, B.temperature AS B_temperature
FROM equipment AS equip
JOIN sensor sen
ON sen.sensor = equip.sensor
AND sen.service = 1
JOIN A
ON A.id = equip.id
AND (A.time>= '1027' AND A.time<= '1031')
LEFT JOIN B
ON B.id = equip.id
AND (B.time>= '1027' AND B.time<= '1031')
AND B.time= A.time
WHERE equip.equip = 8
UNION
SELECT DISTINCT COALESCE(B.time, A.time) time, A.temperature AS A_temperature, B.temperature AS B_temperature
FROM equipment AS equip
JOIN sensor sen
ON sen.sensor = equip.sensor
AND sen.service = 1
LEFT JOIN B
ON B.id = equip.id
AND (B.time>= '1027' AND B.time<= '1031')
AND B.time= A.time
JOIN A
ON A.id = equip.id
AND (A.time>= '1027' AND A.time<= '1031')
WHERE equip.equip = 8
ORDER BY time ASC;
But I did not get the results I wanted.
time | A_temperature | B_temprature
1027 | 30 | NULL
1027 | NULL | 40
1028 | 30 | NULL
1028 | NULL | NULL
1029 | NULL | 50
....
After executing the above query, A.time and B.time are separated and the result is output. I want to combine these at the same time. If time is null, we want to put it in non-null time.
I think you can simplify the answer from your previous question by creating a subquery which just selects all the distinct time values from tables A and B. Then you can separately JOIN the A and B tables to it and GROUP BY the time values, using MAX to aggregate the temperatures since the values will be either valid or NULL, which MAX will ignore:
SELECT t.time, MAX(A.temperature) AS A_temperature, MAX(B.temperature) AS B_temperature
FROM equipment e
JOIN sensor s ON s.sensor = e.sensor AND s.service = 1
JOIN (SELECT time FROM A UNION SELECT time FROM B) t
LEFT JOIN A on A.id = e.id AND A.time = t.time
LEFT JOIN B on B.id = e.id AND B.time = t.time
WHERE t.time BETWEEN 1027 AND 1031
GROUP BY t.time
ORDER BY t.time
Output:
time A_temperature B_temperature
1027 30 40
1028 30
1029 50
1030 60
1031 59
Demo
First, I have 3 table.
Table 1 data:
tid | type_1 | address_1 | contact_1
----+--------+-----------+-----------+
1 | 4 | No.2123 | 01234567
4 | 4 | No.4567 | 00011234
Table 2 data:
tid | type_2 | address_2 | contact_2
----+--------+-----------+-----------+
2 | 3 | No.8888 | 7654321
Table 3 data:
tid | subject | desc
----+---------+-------------+
1 | Test 1 | Desc 1
2 | Test 2 | Desc 2
3 | Test 3 | Desc 3
4 | Test 4 | Desc 4
I would like to combine like this:
tid | subject | type_1 | type_2 | address_1 | address_2 | contact_1 | contact_2 | desc
----+---------+--------+--------+-----------+-----------+-----------+-----------+------
1 | Test 1 | 4 | | No.2123 | |01234567 | | Desc1
2 | Test 2 | | 3 | |No.8888 | |7654321 | Desc2
4 | Test 4 | 4 | | No.4567 | |00011234 | | Desc4
if table 1 got "tid" 1, then table 2 never got the same tid.
Ignore if tid no found in table 1 and table 2
Thanks!
My code before is:
$query = mysql_query("SELECT table1.*, table2.*, table3.subject, table3.desc, FROM table1 ON table1.tid = table3.tid LEFT JOIN table2 ON table2.tid = table3.tid LEFT JOIN table3 ON table1.tid AND table2.tid = table3.tid");
SELECT
table1.tid,
table3.subject,
table1.type_1,
NULL AS type_2,
table1.address_1,
NULL AS address_2,
table1.contact_1,
NULL AS contact_2,
table3.`desc`
FROM table1
INNER JOIN table3 ON table1.tid=table3.tid
UNION ALL
SELECT
table2.tid,
table3.subject,
NULL AS type_1,
table2.type_2,
NULL AS address_1,
table2.address_2,
NULL AS contact_1,
table2.contact_2,
table3.`desc`
FROM table2
INNER JOIN table3 ON table2.tid=table3.tid
DROP TABLE IF EXISTS table1;
CREATE TABLE table1
(tid INT NOT NULL
,type_1 INT NOT NULL
,address_1 VARCHAR(12) NOT NULL
,contact_1 VARCHAR(12) NOT NULL
);
INSERT INTO table1 VALUES
(1 ,4 ,'No.2123','01234567'),
(4 ,4 ,'No.4567','00011234');
DROP TABLE IF EXISTS table2;
CREATE TABLE table2
(tid INT NOT NULL
,type_2 INT NOT NULL
,address_2 VARCHAR(12) NOT NULL
,contact_2 VARCHAR(12) NOT NULL
);
INSERT INTO table2 VALUES
(2 ,3 ,'No.8888','7654321');
DROP TABLE IF EXISTS table3;
CREATE TABLE table3
(tid INT NOT NULL
,subject VARCHAR(12) NOT NULL
,description VARCHAR(12) NOT NULL
);
INSERT INTO table3 VALUES
(1 ,'Test 1','Desc 1'),
(2 ,'Test 2','Desc 2'),
(3 ,'Test 3','Desc 3'),
(4 ,'Test 4','Desc 4');
SELECT x.*
, y.subject
, y.description
FROM
( SELECT tid
, type_1
, NULL type_2
, address_1
, contact_1
, NULL address_2
, NULL contact_2
FROM table1
UNION
SELECT tid
, NULL
, type_2
, NULL
, NULL
, address_2
, contact_2
FROM table2
) x
JOIN table3 y
ON y.tid = x.tid;
+-----+--------+--------+-----------+-----------+-----------+-----------+---------+-------------+
| tid | type_1 | type_2 | address_1 | contact_1 | address_2 | contact_2 | subject | description |
+-----+--------+--------+-----------+-----------+-----------+-----------+---------+-------------+
| 1 | 4 | NULL | No.2123 | 01234567 | NULL | NULL | Test 1 | Desc 1 |
| 2 | NULL | 3 | NULL | NULL | No.8888 | 7654321 | Test 2 | Desc 2 |
| 4 | 4 | NULL | No.4567 | 00011234 | NULL | NULL | Test 4 | Desc 4 |
+-----+--------+--------+-----------+-----------+-----------+-----------+---------+-------------+
3 rows in set (0.00 sec)
The JOIN clause you have written is not in correct format. You should use it like this:
`table1 JOIN table2 ON` your conditions to join the table.
You can get more about JOIN from here.
So you can try this:
SELECT table3.tid AS tid,
t3.subject AS subject,
t1.type_1 AS type_1,
t2.type_2 AS type_2,
t1.address_1 AS address_1,
t2.address_2 AS address_2,
t1.contact_1 AS contact_1,
t2.contact_2 AS contact_2,
t3.desc AS description
FROM table3 t3
INNER JOIN table1 t1 ON t3.tid = t1.tid
INNER JOIN table2 t2 ON t3.tid = t2.tid
This is a small snippet of my table, which currently contains ~10,000,000 rows
+---------+---------------------+-----------+----------------+
| card_id | date | avg_price | foil_avg_price |
+---------+---------------------+-----------+----------------+
| 10000 | 2014-06-28 09:05:56 | 5.02 | 10.22 |
| 20000 | 2014-06-28 09:05:54 | 14.58 | 25.10 |
| 10000 | 2014-06-29 09:05:56 | 0.00 | 19.62 |
| 20000 | 2014-06-29 09:05:54 | 14.58 | 0.00 |
| 10000 | 2014-07-01 09:05:56 | 0.00 | 19.62 |
| 20000 | 2014-07-01 09:05:54 | 0.00 | 25.10 |
+---------+---------------------+-----------+----------------+
It is a price history for cards, including what the avg_price and what the foil_avg_price was for each day or so.
I'd like to select, for a group of card id's the most recent date when the foil_avg_price was > 0, what that price was, and the most recent date that the avg_price was > 0, and what that price was. My resulting data set for the above would look something like this:
+---------+---------------------+-----------+---------------------+----------------+
| card_id | avg_date | avg_price | foil_date | foil_avg_price |
+---------+---------------------+-----------+---------------------+----------------+
| 10000 | 2014-06-28 09:05:56 | 5.02 | 2014-07-01 09:05:54 | 19.62 |
| 20000 | 2014-06-29 09:05:54 | 14.58 | 2014-07-01 09:05:54 | 25.10 |
+---------+---------------------+-----------+---------------------+----------------+
I'm sure that this involves an INNER JOIN on the same table but I can't quite get my head around it. Any help would be much appreciated.
Three steps:
Find last price date
Find last foil price date
resolve prices on these dates
So,
SELECT dates.*, price.avg_price, foilprice.foil_avg_price
FROM (
SELECT
card_id,
MAX(IF(avg_price>0, `date`, '0001-01-01')) AS avg_date,
MAX(IF(foil_avg_price>0, `date`, '0001-01-01')) AS foil_avg_date
FROM card_price
GROUP BY card_id
) AS dates
INNER JOIN card_price AS price
ON dates.card_id=price.`date`
INNER JOIN card_price AS foilprice
ON dates.card_id=foilprice.`date`
Try this query
SELECT A.card_id,max(date),MAX(avg_price), (SELECT MAX(date) FROM test WHERE card_id = A.card_id AND foil_avg_price = MAX(A.foil_avg_price)) AS date,MAX(foil_avg_price) FROM test A
GROUP BY A.card_id
How about if you had 20,000,000 rows...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(card_id INT NOT NULL
,date DATETIME NOT NULL
,price_type VARCHAR(20) NOT NULL
,price_value DECIMAL(5,2) NOT NULL
,PRIMARY KEY(card_id,date,price_type)
);
INSERT INTO my_table VALUES
(10000,'2014-06-28 09:05:56','avg_price',5.02),
(20000,'2014-06-28 09:05:54','avg_price',14.58),
(10000,'2014-06-29 09:05:56','avg_price',0.00),
(20000,'2014-06-29 09:05:54','avg_price',14.58),
(10000,'2014-07-01 09:05:56','avg_price',0.00),
(20000,'2014-07-01 09:05:54','avg_price',0.00),
(10000,'2014-06-28 09:05:56','foil_avg_price',10.22),
(20000,'2014-06-28 09:05:54','foil_avg_price',25.10),
(10000,'2014-06-29 09:05:56','foil_avg_price',19.62),
(20000,'2014-06-29 09:05:54','foil_avg_price',0.00),
(10000,'2014-07-01 09:05:56','foil_avg_price',19.62),
(20000,'2014-07-01 09:05:54','foil_avg_price',25.10);
SELECT x.*
FROM my_table x
JOIN
( SELECT card_id,price_type,MAX(date) max_date FROM my_table WHERE price_value > 0 GROUP BY card_id,price_type) y
ON y.card_id = x.card_id
AND y.price_type = x.price_type
AND y.max_date = x.date;
+---------+---------------------+----------------+-------------+
| card_id | date | price_type | price_value |
+---------+---------------------+----------------+-------------+
| 10000 | 2014-06-28 09:05:56 | avg_price | 5.02 |
| 10000 | 2014-07-01 09:05:56 | foil_avg_price | 19.62 |
| 20000 | 2014-06-29 09:05:54 | avg_price | 14.58 |
| 20000 | 2014-07-01 09:05:54 | foil_avg_price | 25.10 |
+---------+---------------------+----------------+-------------+
Try this:
SELECT a.card_id, a.avg_date, a.avg_price, b.foil_date, b.foil_avg_price
FROM (SELECT c.card_id, c.date AS avg_date, c.avg_price
FROM cards c
INNER JOIN (SELECT c.card_id, MAX(IF(c.avg_price > 0, c.date, NULL)) avg_date
FROM cards c GROUP BY c.card_id
) a ON c.card_id = a.card_id AND c.date = a.avg_date
) AS a
LEFT JOIN (SELECT c.card_id, c.date AS foil_date, c.foil_avg_price
FROM cards c
INNER JOIN (SELECT c.card_id, MAX(IF(c.foil_avg_price > 0, c.date, NULL)) foil_date
FROM cards c GROUP BY c.card_id
) a ON c.card_id = a.card_id AND c.date = a.foil_date
) AS b ON a.card_id = b.card_id ;
OR
SELECT a.card_id, a.avg_date, a.avg_price, b.foil_date, b.foil_avg_price
FROM (SELECT *
FROM (SELECT c.card_id, c.date, c.avg_price
FROM cards c WHERE c.avg_price > 0
ORDER BY c.date DESC
) AS A
GROUP BY A.date
) AS a
LEFT JOIN ( SELECT *
FROM (SELECT c.card_id, c.date, c.foil_avg_price
FROM cards c WHERE c.foil_avg_price > 0
ORDER BY c.date DESC
) AS B
GROUP BY B.date
) AS b ON a.card_id = b.card_id;
I have a table with a composite primary key on EID (event ID) and start_time. I have another column called attending.
Users make their events more popular by reusing the event ID and changing the date, however, I create a new line in the database in this instance.
I would like to create a 4th column, actual_attending which is equal to the attending value minus the previous event's attending value. If their is no previous ID, the column can be null. How can I calculate this via update.
Here is a sqlfiddle as an example: http://sqlfiddle.com/#!2/43f2c5
update event e1
set e1.actual_attending = (select e1.attending - e2.attending
from event e2
where e2.eid(+) = e1.previous_eid
)
SELECT a.*
, a.attending-b.attending new_actual_attending
FROM
( SELECT x.*
, COUNT(*) rank
FROM event x
JOIN event y
ON y.eid = x.eid
AND y.start_time <= x.start_time
GROUP
BY eid, start_time
) a
LEFT
JOIN
( SELECT x.*
, COUNT(*) rank
FROM event x
JOIN event y
ON y.eid = x.eid
AND y.start_time <= x.start_time
GROUP
BY eid, start_time
) b
ON b.eid = a.eid
AND b.rank = a.rank - 1;
+-----+------------+-----------+------------------+------+----------------------+
| eid | start_time | attending | actual_attending | rank | new_actual_attending |
+-----+------------+-----------+------------------+------+----------------------+
| 1 | 2013-06-08 | 29 | NULL | 1 | NULL |
| 2 | 2013-06-09 | 72 | NULL | 1 | NULL |
| 2 | 2013-06-16 | 104 | NULL | 2 | 32 |
| 3 | 2013-06-07 | 224 | NULL | 1 | NULL |
| 3 | 2013-06-14 | 222 | NULL | 2 | -2 |
+-----+------------+-----------+------------------+------+----------------------+
http://sqlfiddle.com/#!2/43f2c5/2