Get two previous and next rows data from current id SQL PHP - php

i have a table with database and i want get two data before current id and get two data after current id.
primary_key id
1 345
2 356
3 400
4 102
5 210
6 190
Case:
If current id defined 400, the results as before is 356 & 345 and
after is 102 & 210
If current id defined 210, the results as before is 102 & 400 and
after is 190
If current id defined 356, the results as before is 345 and
after is 400 & 102
If current id defined 345, the results as before is NULL and
after is 356 & 400
If current id defined 190, the results as before is 210 & 102 and
after is NULL
I try this SQL but not working fine,
$define_id = 400;
SELECT *
FROM table_name
WHERE (
id = IFNULL(
(
SELECT MIN(id)
FROM table_name
WHERE id > $define_id
), 0 )
OR id = IFNULL(
(
SELECT MAX(id)
FROM table_name
WHERE id < $define_id
), 0 )
)
LIMIT 2
The code success to get before and after data, but only one before and one after. I want get result two before and two after.
Please help.

Tried with the outputs you mentioned in the question. If this is not you want please explain more clearly.
CREATE TABLE IF NOT EXISTS `docs` (
`primary_key` int(6) unsigned NOT NULL,
`id` int(3) unsigned NOT NULL,
PRIMARY KEY (`primary_key`,`id`)
) DEFAULT CHARSET=utf8;
INSERT INTO `docs` (`primary_key`, `id`) VALUES
('1', '345'),
('2', '356'),
('3', '400'),
('4', '102'),
('5', '210'),
('6', '190');
Query
(SELECT * FROM docs WHERE primary_key < (SELECT primary_key FROM docs WHERE id = 400) ORDER BY primary_key DESC LIMIT 2)
UNION ALL
(SELECT * FROM docs WHERE primary_key > (SELECT primary_key FROM docs WHERE id = 400) ORDER BY primary_key ASC LIMIT 2);
Sqlfiddle link http://sqlfiddle.com/#!9/e11c8d/2

You can do it with min and max functions, So for next
SELECT * FROM `table` WHERE id = (select min(id) from `table` where id > YOUR_ID)
And for previous:
SELECT * from `table` where id = (select max(id) from table where id < YOUR_ID)

One method uses union all for the two groups:
(select 'before' as which, t.*
from t cross join
(select primary_key from t where id = $define_id) x
where t.primary_key < x.primary_key
order by t.primary_key desc
limit 2
) union all
(select 'after' as which, t.*
from t cross join
(select primary_key from t where id = $define_id) x
where t.primary_key > x.primary_key
order by t.primary_key asc
limit 2
);

Related

select tow row same time in one table

I have this table:
USERID CHECKTIME CHECKTYPE
1 2018-10-21 09:01:08.000 I
1 2018-10-21 10:00:51.000 I
1 2018-10-22 13:19:23.000 1
1 2018-10-22 15:56:11.000 0
1 2018-10-22 16:51:27.000 O
I need select two rows like this:
userid in out
1 2018-10-22 13:19:23.000 2018-10-22 15:56:11.000
but same the date and same userid
date and userid come from post page php
database is sqlserver connection with php
SELECT TOP 1 checktime, userid, checktype
FROM CHECKINOUT
WHERE userid = '$idd'
AND CAST(checktime As DATE)='$x'
AND checktype='1'
UNION ALL
SELECT TOP 1 checktime, userid, checktype
FROM CHECKINOUT
WHERE userid='$idd'
AND CAST(checktime As DATE)='$x'
AND checktype='0'
Here you go
CREATE TABLE T
([UserId] int, [CheckDate] datetime, [CheckType] BIT)
;
INSERT INTO T
([UserId], [CheckDate], [CheckType])
VALUES
(1, '2018-10-21 09:01:08', 1),
(1, '2018-10-21 10:00:51', 1),
(1, '2018-10-22 13:19:23', 1),
(1, '2018-10-22 15:56:11', 0),
(1, '2018-10-22 16:51:27', 0)
;
SELECT DISTINCT UserID,
(SELECT MAX(CheckDate) FROM T WHERE CheckType = 1 ANDUserID = TT.UserID) [In],
(SELECT MIN(CheckDate) FROM T WHERE CheckType = 0 AND UserID = TT.UserID) [Out]
FROM T TT
WHERE UserID = 1;
You have wrong data in your sample (I guess), cause in your query you check for '1' and '0' in your query, which mean there is no 'I', so you can use BIT datatype instead of VARCHAR.
If you really have 'I' in the check type so it would be
SELECT DISTINCT UserID,
(SELECT MAX(CheckDate) FROM T WHERE CheckType = '1' AND UserID = TT.UserID) [In],
(SELECT MIN(CheckDate) FROM T WHERE CheckType = '0' AND UserID = TT.UserID) [Out]
FROM T TT
WHERE UserID = 1;
Results
+--------+---------------------+---------------------+
| UserID | In | Out |
+--------+---------------------+---------------------+
| 1 | 22/10/2018 13:19:23 | 22/10/2018 15:56:11 |
+--------+---------------------+---------------------+
Table structure
USERID : int(11)
CHECKTIME : datetime
CHECKTYPE : char(2)
Query
SELECT USERID, MAX(`IN`) AS 'IN', MAX(`OUT`) AS 'OUT'
FROM
(
SELECT USERID
, CASE WHEN CHECKTYPE = '1' THEN CHECKTIME ELSE '0000-00-00 00:00:00.000' END AS 'IN'
, CASE WHEN CHECKTYPE = '0' THEN CHECKTIME ELSE '0000-00-00 00:00:00.000' END AS 'OUT'
FROM CHECKINOUT
WHERE CHECKTYPE IN ('0', '1')
) U
GROUP BY U.USERID
I checked in my local and this query worked well.
Explanation
Sub query U
It collect CHECKTIME for IN, and OUT by CHECKTYPE condition. When no IN data, the default value will be "0000:00:00 ....", and OUT is also.
In outer query
It will get max value (reject "0000:00:00 ..."), and group records, so we can get correct data.
I suggest using GROUP BY instead of DISTINCT, because it makes Query run faster than DISTINCT.

Mysql limit results based on value from column

I'm trying to limit the number of results of a query based on another table column. For example, I have a table for products and a config table, like this:
tb_product
id | active | name | value | ...
tb_config
max_product | ...
What I'd like to do is something like this
SELECT
a.name, a.value
FROM
tb_product a,
tb_config b
WHERE a.active = 1
LIMIT b.max_product
But I'm getting errors like #1327 - Undeclared variable: b. Is there a way to achieve this result?
Because currently what I'm doing is doing another query to get just the max_product value and then use it as php variable to limit the results, like this:
$limit = "SELECT max_product FROM tb_config";
SELECT name, value FROM tb_product WHERE ativo = 1 LIMIT $limit
Maybe....
SELECT a.name
, a.value
FROM tb_product a
CROSS JOIN (SELECT #Limit:=(SELECT max_product from tb_config))
WHERE a.active = 1
LIMIT #Limit
With help from #ENargit's answer in Variable LIMIT Clause in MySQL, you can do it using a row count variable.
Assuming the following schema:
CREATE TABLE
tb_product
(
id INT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(10),
`value` VARCHAR(10)
);
INSERT INTO
`tb_product`
(`name`, `value`)
VALUES
('Name1','Value1'),
('Name2','Value2'),
('Name3','Value3'),
('Name4','Value4'),
('Name5','Value5'),
('Name6','Value6'),
('Name7','Value7'),
('Name8','Value8'),
('Name9','Value9'),
('Name10','Value10');
CREATE TABLE
`tbl_config`
(
id INT PRIMARY KEY AUTO_INCREMENT,
`type` VARCHAR(10),
`value` INT
);
INSERT INTO
`tbl_config`
(`type`,`value`)
VALUES
('something',10),
('maxrows',7);
You can reference the config table with a subquery:
SELECT * FROM (
SELECT
tb_product.*,
#rownum := #rownum + 1 AS RowNum
FROM tb_product,
(SELECT #rownum := 0) AS CounterTbl
) AS DataTbl
WHERE
RowNum <= (
SELECT
`value`
FROM
`tbl_config`
WHERE
`type` = 'maxrows'
);
Gives you the first 7 rows (according to the config value). You can obviously extend this to do sorting etc.
SQLFiddle: http://www.sqlfiddle.com/#!9/f789e4/2

How Can Identify Table in Mysql Union All

I Have Query As the following
SELECT * FROM (
(
SELECT * FROM comments
WHERE user_id = '66' AND product_id = '3'
AND status = 1
)
UNION ALL
(
SELECT * FROM comments_reply
WHERE user_id = '66' AND product_id = '3'
AND status = 1 )
) results
ORDER BY datetime DESC
Which Gets Data from two tabel and shows it in order to datetime.now frontside i am showing this data. but now i wants to know that which data is came from which table.
because I am calling a modal to show a comment in modal by this
<span class="showcomment text-danger"
data-cid = <?=$row[0]?>>view Comment</span>
But $row[0] Gives me an id But How can i identify that this id is came from which Table ?
I would rewrite your query as with tablename that you want
SELECT 'comments' as table_name, col1, col2,... coln FROM comments c
WHERE user_id = '66' AND product_id = '3'
AND status = 1 UNION ALL
SELECT 'comments_reply' as table_name, col1, col2,... coln FROM comments_reply cr
WHERE user_id = '66' AND product_id = '3'
ORDER BY col DESC
And, i suspect if you have user_id, product_id has numeric type then you should use only values without single quote.
where user_id = 66 AND product_id = 3
You can add a column to each of your two tables and populate it with a custom string. For example, the first sub query would start like the following with the new column “table_name”... select *, ‘comments’ table_name from comments...
SELECT * FROM (
(
SELECT "comments" as tablename,* FROM comments
WHERE user_id = '66' AND product_id = '3'
AND status = 1
)
UNION ALL
(
SELECT "comments_reply" as tablename,* FROM comments_reply
WHERE user_id = '66' AND product_id = '3'
AND status = 1 )
) results
ORDER BY datetime DESC

MySQL SELECT query get record if is_default = 1 otherwise is_default = 0?

My image table look like this:
id product_id image is_default
1 11 test.jpg 0
2 11 make.jpg 0
3 11 tkae.jpg 1
4 11 value.jpg 0
5 11 mcate.jpg 0
I want to fetch a default image if is_default 1 is not exists the fetch first image. I try this query but not success.
SELECT *
FROM `images`
WHERE `product_id` = '11'
GROUP BY `product_id`
ORDER BY `is_default` DESC
Can anyone help me to write that query.
A simpler
SELECT *
FROM `images`
WHERE `product_id` = '11'
ORDER BY `is_default` DESC
LIMIT 1
should work.

Mysql replace two value in one query?

BEFORE
id | cat_id | order
33 | 1 | 1
34 | 1 | 2
AFTER
id | cat_id | order
33 | 1 | 2
34 | 1 | 1
Now using 4 query
$db is wrap $mysqli for using placeholder and injection defense
get first record by id
$curr = $db->q('SELECT id,order,cat_id FROM `tbl` WHERE id`=? FOR UPDATE',
33)->fetch_assoc();
if exist first record find next record by order field
if($curr){
$next = $db->q('SELECT id,order FROM `tbl` WHERE `cat_id`=? AND
`order`>? ORDER BY `order` LIMIT 1 FOR UPDATE',
$curr['cat_id'],$curr['order']));
if exist first and second recorn change order value
if($prev['id']){
$db->q("UPDATE `tbl` SET `order`=? WHERE `id`=?",$next['order'],$curr['id']);
$db->q("UPDATE `tbl` SET `order`=? WHERE `id`=?",$curr['order'],$next['id']);
}
}
Important! Checking exist two record, lock rows for update
MySQL doesn't support update with the same table in the FROM statement. So because of this there are (select * from TBL) as t2 in inner subqueries.
Also EXISTS condition in the first CASE WHEN is to prevent update if the second record doesn't exists ("if exist first and second records change order value")
Here is a SQLfiddle example
UPDATE tbl as t1
SET `order`=
CASE WHEN id = 33
and
EXISTS (SELECT ID from (select * from TBL) t2 where
cat_id=t1.Cat_Id
and `order`>t1.`order`
ORDER BY `order`
LIMIT 1)
THEN
(SELECT `order` from (select * from TBL) t2 where
cat_id=t1.Cat_Id
and `order`>t1.`order`
ORDER BY `order`
LIMIT 1)
WHEN id <>33 THEN
(SELECT `order` from (select * from TBL) t2 where
cat_id=t1.Cat_Id
and `order`<t1.`order`
ORDER BY `order` DESC
LIMIT 1 )
ELSE `order`
END
where id =33
or
(SELECT ID from (select * from TBL) t2 where
cat_id=t1.Cat_Id
and `order`<t1.`order`
ORDER BY `order` DESC
LIMIT 1) =33
With one query it's:
UPDATE
`tbl`
SET
`order`=CASE
WHEN `order`=2 THEN 1
WHEN `order`=1 THEN 2
END;
WHERE
`order` IN (1,2)
or, for id's condition:
UPDATE
`tbl`
SET
`order`=CASE
WHEN `order`=2 THEN 1
WHEN `order`=1 THEN 2
END;
WHERE
id = $id
To swap 2 fields by row id try:
UPDATE `tbl` AS tbl1
JOIN `tbl` AS tbl2 ON ( tbl1.id = 33 AND tbl2.id = 34 )
SET
tbl1.order = tbl2.order, tbl2.order = tbl1.order
Also you can set your desired value instead of swap between 2 fileds.
If needed, you can add a where clause like below to swap where cat_id are 1 in two rows:
WHERE
tbl1.cat_id = 1 AND tbl2.cat_id = 1
Update:
If your order numbers are unique for any cat_id you can try this way:
UPDATE `tbl` AS tbl1
JOIN `tbl` AS tbl2 ON ( tbl1.order = 1 AND tbl2.order = 2 )
SET
tbl1.order = tbl2.order, tbl2.order = tbl1.order
WHERE
tbl1.cat_id = 1 AND tbl2.cat_id = 1
It works if your order field is int, Otherwise you should quote order values in query.
See the result on SQLFiddle

Categories