I have a database similar to the following...
ID ! Value
...
654 ! Blue
656 ! Green
659 ! Blue
665 ! Blue
...
I have the value of a numerical primary key. I'm wanting to do a query that pulls the previous and next primary keys that match a pattern.
For example, I have ID 659, I want to do something like
SELECT NEXT, PREV FROM TABLE WHERE VALUE = 'Blue';
And I'm trying to do it efficiently instead of fetching the whole table.
1 row and 2 columns:
SELECT
(SELECT ID FROM test WHERE ID < 659 AND Value = 'Blue' ORDER BY ID DESC LIMIT 1) AS PREV,
(SELECT ID FROM test WHERE ID > 659 AND Value = 'Blue' ORDER BY ID ASC LIMIT 1) AS NEXT;
2 rows and 1 column:
(SELECT ID AS PREV FROM test WHERE ID < 659 AND Value = 'Blue' ORDER BY ID DESC LIMIT 1)
UNION
(SELECT ID AS NEXT FROM test WHERE ID > 659 AND Value = 'Blue' ORDER BY ID ASC LIMIT 1);
DROP TABLE IF EXISTS T;
CREATE TABLE T (ID INT PRIMARY KEY,VAL VARCHAR(10));
INSERT INTO T (ID,VAL) VALUES (654,'BLUE'),(656,'GREEN'),(659,'BLUE'),(665,'BLUE');
SELECT CURRENT.ID CURRENTID,CURRENT.VAL,
(SELECT MIN(ID) FROM T NEXT WHERE NEXT.ID > CURRENT.ID AND NEXT.VAL = CURRENT.VAL) NEXT,
(SELECT MAX(ID) FROM T PREV WHERE PREV.ID < CURRENT.ID AND PREV.VAL = CURRENT.VAL) PREV
FROM T CURRENT;
+-----------+-------+------+------+
| CURRENTID | VAL | NEXT | PREV |
+-----------+-------+------+------+
| 654 | BLUE | 659 | NULL |
| 656 | GREEN | NULL | NULL |
| 659 | BLUE | 665 | 654 |
| 665 | BLUE | NULL | 659 |
+-----------+-------+------+------+
4 rows in set (0.00 sec)
Or less elegantly (can't say about performance)
SELECT CURRENT.ID CURRENTID,CURRENT.VAL,
NEXT.ID NEXTID,PREV.ID PREVID
FROM
(
SELECT T.ID, T.VAL, IF(T.VAL<>#P,#RN:=1,#RN:=#RN+1) RN, #P:=T.VAL
FROM T,(SELECT #RN:=0,#P:=0) R
ORDER BY T.VAL,T.ID
) CURRENT
LEFT JOIN
(
SELECT T.ID, T.VAL, IF(T.VAL<>#P1,#RN1:=1,#RN1:=#RN1+1) RN1, #P1:=T.VAL
FROM T,(SELECT #RN1:=0,#P1:=0) R1
ORDER BY T.VAL,T.ID
) NEXT ON NEXT.VAL = CURRENT.VAL AND NEXT.RN1 = CURRENT.RN + 1
LEFT JOIN
(
SELECT T.ID, T.VAL, IF(T.VAL<>#P2,#RN2:=1,#RN2:=#RN2+1) RN2, #P2:=T.VAL
FROM T,(SELECT #RN2:=0,#P2:=0) R2
ORDER BY T.VAL,T.ID
) PREV ON PREV.VAL = CURRENT.VAL AND PREV.RN2 = CURRENT.RN - 1
;
+-----------+-------+--------+--------+
| CURRENTID | VAL | NEXTID | PREVID |
+-----------+-------+--------+--------+
| 654 | BLUE | 659 | NULL |
| 659 | BLUE | 665 | 654 |
| 665 | BLUE | NULL | 659 |
| 656 | GREEN | NULL | NULL |
+-----------+-------+--------+--------+
4 rows in set (0.00 sec)
Related
enter image description hereAs shown in the image below, we have 6 records for same vehicle_id (3 IN,3 OUT on different dates).
I need result as :
ID vehicle_id IN OUT
1 X first_record second_record
2 x third_record fourth_record
3 x fifth_record sixth_record
So,for one record one IN time and one OUT time.
Is it possible to get with select query or do I need to write a stored proc?
You could use sub queries with a limit clause for example
drop table if exists t;
create table t(id int auto_increment primary key, vid int, trip_status varchar(3),dt datetime);
insert into t (vid,trip_status,dt)
values
(1,'in','2018-12-01 01:00:00'),
(1,'out','2018-12-01 02:00:00'),
(1,'in','2018-12-01 03:00:00'),
(1,'out','2018-12-01 04:00:00'),
(1,'in','2018-12-01 05:00:00'),
(1,'in','2018-12-01 05:00:00');
select t.*
, (select case when t1.trip_status ='out' then trip_status
else concat(t1.trip_status, '**Error**')
end
from t t1 where t1.vid = t.vid and t1.id > t.id order by t1.id limit 1) nexttrip_status
, (select t1.dt from t t1 where t1.vid = t.vid and t1.id > t.id order by t1.id limit 1) next_dt
from t where trip_status = 'in';
+----+------+-------------+---------------------+-----------------+---------------------+
| id | vid | trip_status | dt | nexttrip_status | next_dt |
+----+------+-------------+---------------------+-----------------+---------------------+
| 1 | 1 | in | 2018-12-01 01:00:00 | out | 2018-12-01 02:00:00 |
| 3 | 1 | in | 2018-12-01 03:00:00 | out | 2018-12-01 04:00:00 |
| 5 | 1 | in | 2018-12-01 05:00:00 | in**Error** | 2018-12-01 05:00:00 |
| 6 | 1 | in | 2018-12-01 05:00:00 | NULL | NULL |
+----+------+-------------+---------------------+-----------------+---------------------+
4 rows in set (0.00 sec)
Click here This image show the sql records as per your questions
This is the output as you expect.
SQL = "Select x.id, x.vehicle_id, x.time as in_time, (Select y.time from xx.new_table as y where y.id =x.id+1) as outtime from xx.new_table as x where x.id % 2 = 1"
Please note "where x.id % 2 = 1" this condition you have to make it dynamic. Sometimes you have to set = 0 or = 1 based on your ID of the record. For that, you need to write addition select SQL to check it. Hope this will help you.
Click Here To See output screen shot
I want two display the result of the second table 'e_value', wich are two records (from only one column), as two columns for the select query from first table 'e_order_item'.
Also I am displaying many order items using a parameter 'collect_id',
so I want to display each two values of the table 'e_value' using to the order item id displayed on the select query.
for example, I have this on the tables
+-------------------------------+
| e_order_item |
+-------------------------------+
| oi_id oi_price oi_collect_id |
| 1 100 2 |
| 2 30 2 |
| 3 55 3 |
| 4 70 4 |
| 5 220 2 |
| 6 300 2 |
+-------------------------------+
+----------------------------+
| e_value |
+----------------------------+
| v_id v_value v_oi_id |
| 1 name1 1 |
| 2 surname1 1 |
| 3 name2 2 |
| 4 surname2 2 |
| 5 name3 5 |
| 6 surname3 5 |
+----------------------------+
I want to select the order_items that have collect_id = 2, and I want the result to be like this
+--------------------------------------------------+
| |
+--------------------------------------------------+
| Result |
| oi_id oi_price oi_collect_id name surname |
| 1 100 2 name1 surname1 |
| 2 30 2 name2 surname2 |
| 5 220 2 name3 surname3 |
| 6 300 2 null null |
| |
+--------------------------------------------------+
Here's the query:
SELECT
t.oi_id,
t.oi_price,
t.oi_collect_id,
LEFT (
GROUP_CONCAT(t.v_value),
IF (
LOCATE(',',GROUP_CONCAT(t.v_value)) = 0,
LENGTH(GROUP_CONCAT(t.v_value)),
LOCATE(',', GROUP_CONCAT(t.v_value)) - 1
)
) 'Name',
RIGHT (
GROUP_CONCAT(t.v_value),
LENGTH(GROUP_CONCAT(t.v_value)) -
IF (
LOCATE(',',GROUP_CONCAT(t.v_value)) = 0,
LENGTH(GROUP_CONCAT(t.v_value)),
LOCATE(',',GROUP_CONCAT(t.v_value))
)
) Surname
FROM
(
SELECT
*
FROM e_order_item
LEFT JOIN e_value ON e_order_item.oi_id = e_value.v_oi_id
WHERE e_order_item.oi_collect_id = 2
ORDER BY oi_id, v_id
) t
GROUP BY t.oi_id;
DEMO HERE
Note:
The following example illustrates how we can get the first string and second string from a comma separated string.
SET #str := 'A,BCDEFGHIJKL';
SELECT
LEFT(#str,IF(LOCATE(',',#str) = 0, LENGTH(#str),LOCATE(',',#str)-1)) AS StringBeforeComma,
RIGHT(#str,LENGTH(#str)-IF(LOCATE(',',#str)=0,LENGTH(#str),LOCATE(',',#str))) AS StringAfterComma
Result:
StringBeforeComma StringAfterComma
A BCDEFGHIJKL
You have to go for pivoting to get the desired result.
select oi_id, oi_price, oi_collect_id
, max(name) as name
, max(surname) as surname
from (
select
i.oi_id, i.oi_price, i.oi_collect_id
, case when #prevVal <> (#currVal:=v.v_oi_id)
then v.v_value
else null
end as name
, case when #prevVal = #currVal
then v.v_value
else null
end as surname
, #prevVal:=#currVal as temp_currVal
from e_order_item i
left join e_value v on v.v_oi_id = i.oi_id,
(select #prevVal:=-1, #currVal:=-1) as inits
where i.oi_collect_id=2
) as main_data
group by oi_id, oi_price, oi_collect_id
order by 1;
This is tested and run successfully...and give output as you want...
There are two subqueries:
1.First will give all result having collect_id = 2...
1.SELECT tab1.oi_id, tab1.oi_price, tab1.oi_collect_id
from(
SELECT oi_id, oi_price, oi_collect_id
from e_order_item
where oi_collect_id = 2
) as tab1;
2.This query will give you name, surname and id in different columns..
2.(SELECT e.v_value as name, surname, id
from (
select t1.v_value as surname, t1.v_oi_id as id from e_value as t1
group by t1.v_oi_id
)join e_value as e on id = e.v_oi_id and surname <> e.v_value
) as tab2 on tab1.oi_id = tab2.id;
Now left join these two query to get our desired result as:
SELECT tab1.oi_id, tab1.oi_price, tab1.oi_collect_id, name, surname
from(
SELECT oi_id, oi_price, oi_collect_id
from e_order_item
where oi_collect_id = 2
) as tab1 left join
(SELECT e.v_value as name, surname, id
from (
select t1.v_value as surname, t1.v_oi_id as id from e_value as t1
group by t1.v_oi_id
)join e_value as e on id = e.v_oi_id and surname <> e.v_value
) as tab2 on tab1.oi_id = tab2.id
order by tab1.oi_id asc; // to print in ascending order..
Why we use left join..You can use this link http://www.w3schools.com/sql/sql_join_left.asp to understand properly...
If this solution is helpful then let me know...
I have a table with fields like this
lev_1_id,lev_1_seq,lev_1_new_seq,lev_2_id,lev_2_seq,lev_2_new_seq
284e777e,1,null,b4dce5bb,1,null<br>
284e777e,1,null,dfd158ed,2,null<br>
284e777e,1,null,fedbf511,3,null<br>
0c7e0938,2,null,2333f431,1,null<br>
0c7e0938,2,null,808734fa,2,null<br>
0c7e0938,2,null,2504e0de,3,null<br>
And now I want to update the lev_1_new_seq and, lev_2_new_seq by reversing the values in lev_1_seq and lev_2_seq respectively.
After updating the fields, the table should look like this:
lev_1_id,lev_1_seq,lev_1_new_seq,lev_2_id,lev_2_seq,lev_2_new_seq
284e777e,1,2,b4dce5bb,1,3<br>
284e777e,1,2,dfd158ed,2,2<br>
284e777e,1,2,fedbf511,3,1<br>
0c7e0938,2,1,2333f431,1,3<br>
0c7e0938,2,1,808734fa,2,2<br>
0c7e0938,2,1,2504e0de,3,1<br>
can anyone help me with updating the fields?
Thanks in advance!
This solution works if initial ordering was done by id. 2 - the column you updating, 1 - column that needs to be in reverse order, tmp - table, id - unique key, initial sorting column.
BEGIN
DECLARE p INT;
DECLARE v INT;
DECLARE n INT DEFAULT 0;
DECLARE i INT DEFAULT 0;
SELECT COUNT(*) FROM `tmp` INTO n;
SET i=0;
WHILE i<n DO
select `1` from `tmp` order by `id` desc limit i,1 into p;
select `id` from `tmp` order by `id` limit i,1 into v;
update `tmp` set `2` = p where `id` = v;
SET i = i + 1;
END WHILE;
End
For your table: 1 = lev_1_seq, 2 = lev_1_new_seq, id = lev_1_id
It appears that you should be able to calculate the "new" sequences by deducting the exiting sequences from the relevant maximums + 1.
e.g. the max(lev_1_seq) + 1 = 3, so for an existing value of 2: 3-2 = 1, for an existing value of 1: 3-1 = 2
UPDATE table1 tu
JOIN (
SELECT
t1.lev_1_id
, m1.lev1maxseq
, MAX(t1.lev_2_seq) + 1 lev2maxseq
FROM table1 t1
CROSS JOIN (
SELECT
MAX(lev_1_seq) + 1 lev1maxseq
FROM table1
) m1
GROUP BY
t1.lev_1_id
, m1.lev1maxseq
) nv on tu.lev_1_id = nv.lev_1_id
SET
tu.lev_1_new_seq = (nv.lev1maxseq - tu.lev_1_seq)
, tu.lev_2_new_seq = (nv.lev2maxseq - tu.lev_2_seq)
;
see this sqlfiddle
results:
| lev_1_id | lev_1_seq | lev_1_new_seq | lev_2_id | lev_2_seq | lev_2_new_seq |
|----------|-----------|---------------|----------|-----------|---------------|
| 284e777e | 1 | 2 | b4dce5bb | 1 | 3 |
| 284e777e | 1 | 2 | dfd158ed | 2 | 2 |
| 284e777e | 1 | 2 | fedbf511 | 3 | 1 |
| 0c7e0938 | 2 | 1 | 2333f431 | 1 | 3 |
| 0c7e0938 | 2 | 1 | 808734fa | 2 | 2 |
| 0c7e0938 | 2 | 1 | 2504e0de | 3 | 1 |
I have this table (test):
+----+---------+-----+---+
| ID | Name | A | B |
+----+---------+-----+---+
| 1 | Steve | 200 | 0 |
| 2 | Steve | 200 | 1 |
| 5 | James | 90 | 0 |
| 4 | James | 50 | 1 |
| 3 | Warrick | 100 | 1 |
+----+---------+-----+---+
and this SQL query:
SELECT one.Name as Name, one.A as one_value, zero.A as zero_value
FROM test one LEFT JOIN test zero ON one.Name = zero.Name AND one.A <> zero.A
WHERE zero.B = 0 AND one.B = 1
which returns:
+-------+-----------+------------+
| Name | one_value | zero_value |
+-------+-----------+------------+
| James | 50 | 90 |
+-------+-----------+------------+
But what I want is when a record exists only with B = 1 that it is included in the response with a NULL value or something in the zero_value column, like this:
+---------+-----------+------------+
| Name | one_value | zero_value |
+---------+-----------+------------+
| James | 50 | 90 |
| Warrick | 100 | NULL |
+---------+-----------+------------+
How can I do this?
Edit:
I worked it out:
SELECT one.Name, one.A, zero.A
FROM test one LEFT JOIN test zero ON one.Name = zero.Name AND ( zero.B = 0 OR zero.B is NULL )
WHERE ( one.A <> zero.A OR zero.A is null )
Because of the left join the value of zero.B may be NULL, so you need to extend the WHERE condition:
WHERE one.B=1 AND (zero.B IS NULL OR zero.B = 0)
Update
You should also move the score condition down into WHERE:
WHERE one.B=1 AND (zero.B IS NULL OR zero.B = 0)
AND (zero.A IS NULL OR one.A <> zero.A)
How about this
select Name ,
(case when B= 0 Then A else null) as zero_value,
(case when B= 1 Then A else null) as one_value
from test
LEFT JOIN is a good thing here, this is what you want :
SELECT
one.Name
,one.A as one_value
, zero.A as zero_value
FROM test one
LEFT JOIN test zero
on one.Name = zero.Name
and zero.B = 0
where one.B = 1
Perhaps you want to handle when record exists only with B = 0 that it is included in the response with a NULL value in the one_value column in same time :
SELECT
test.Name
, one.A as one_value
, zero.A as zero_value
FROM
( SELECT Name
FROM test
GROUP BY Name) test
LEFT JOIN test one
on test.Name = one.Name
and one.B = 1
LEFT JOIN test zero
on test.Name = zero.Name
and zero.B = 0
Here is a demo for both queries :)
http://www.sqlfiddle.com/#!2/6332a/7/0
Try thiz query that give exact result for u
SELECT Test.nme,
one.A AS one_value,
zero.A AS zero_value
FROM(SELECT name AS nme FROM test WHERE name NOT in( SELECT one.Name FROM test one LEFT JOIN test zero ON (one.Name = zero.Name AND one.A=zero.A)
WHERE zero.B=0 AND one.B=1)GROUP BY name)Test
LEFT JOIN test one ON test.nme=one.Name AND one.B=1
LEFT JOIN test zero ON test.nme=zero.Name AND zero.B=0;
I just added the 'default' column to my DB. I am trying to set the default value to '1' based on the latest 'addDate' per accountId.
+----+-----------+--------------------+--------+
| id | accountId | addDate | default|
+----+-----------+--------------------+--------+
| 1 | 45 |2012-02-29 08:41:59 | |
| 2 | 55 |2012-03-29 08:41:59 | |
| 3 | 45 |2012-04-29 08:41:59 | |
| 4 | 55 |2012-05-29 08:41:59 | |
| 5 | 60 |2012-05-29 08:41:59 | |
+----+-----------+--------------------+--------+
I found I was able to isolate the proper rows by using =>
select * from tble1
where addDate = (select max(addDate) from tble1 as sl where sl.accountId = tble1.accountId);
I need to be able to run an UPDATE that sets 'default' column to '1' only 1 time per 'accountId' basing it off of latest 'addDate'.
try this
UPdate Table1
SET `default` = 1
where addDate in (select * from (
select max(addDate) from table1 as sl group by accountId)t
)
DEMO HERE
UPDATE table1 x
LEFT
JOIN table1 y
ON y.accountid = x.accountid
AND y.adddate > x.adddate
SET x.default = 1
WHERE y.id IS NULL;
or (faster)
UPDATE table1 x
JOIN
( SELECT accountid
, MAX(addDate) max_adddate
FROM table1
GROUP
BY accountid
) y
ON y.accountId = x.accountId
AND y.max_adddate = x.adddate
SET x.default = 1;