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
Related
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.
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
);
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
I have a query that returns as a result what seems to be a BLOB
SELECT CAST(GROUP_CONCAT(CONCAT(value,'(',qty,')') SEPARATOR ', ') AS CHAR)a
FROM (
SELECT value,count(*) qty
FROM extra_field_values
WHERE fieldid = #fieldid
AND value != ''
AND itemid IN (7,8,10,12,15,16,17,18,19,20,21,22,23,24,25,26)
GROUP BY value
) x
the table is
CREATE TABLE `extra_field_values` (
`itemid` int(11) NOT NULL DEFAULT '0',
`fieldid` int(11) NOT NULL DEFAULT '0',
`value` text NOT NULL,
KEY `itemid` (`itemid`),
KEY `fieldid` (`fieldid`),
KEY `value` (`value`(1)),
KEY `inx` (`itemid`,`fieldid`,`value`(1))
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
The main idea is that i want a string per row returning value:::count separated by ###.
The problem is that php returns empty result. I have tried both mysql and mysqli as far as drivers is concerned.
Any ideas?
PHP
$result = $mysqli->query(" SELECT CAST(GROUP_CONCAT(CONCAT(value,'(',qty,')') SEPARATOR ', ') AS CHAR)a
FROM (
SELECT value,count(*) qty
FROM extra_field_values
WHERE fieldid = #fieldid
AND value != ''
AND itemid IN (7,8,10,12,15,16,17,18,19,20,21,22,23,24,25,26)
GROUP BY value
) x ") ;
while( $row = $result->fetch_assoc() ){
print_r($row);
}
Even if you execute it through phpmyadmin it returns null. Try it on EMS mysql it works like a charm.
full query
SELECT
extra_field_values.fieldid,
extra_fields.var_name,
extra_fields.field,
extra_fields.type,
#fieldid:=extra_field_values.fieldid,
(
SELECT CAST(GROUP_CONCAT(CONCAT(value,'(',qty,')') SEPARATOR ', ') AS CHAR) a
FROM ( SELECT value, #fieldid, count(*) qty FROM extra_field_values WHERE fieldid = #fieldid GROUP BY value ) x
) as v,
count(itemid) as total,
(SELECT groupid FROM extra_fields_groups_items WHERE itemid = #fieldid) as groupid
FROM extra_field_values INNER JOIN extra_fields
ON (extra_field_values.fieldid=extra_fields.fieldid)
WHERE module = 'listings'
AND itemid IN (7,8,10,12,15,16,17,18,19,20,21,22,23,24,25,26)
AND settings LIKE '%"search";s:1:"1"%'
GROUP BY fieldid
You are querying for fields matching the session variable #fieldid but I would guess you're not defining that variable in your mysqli session or in your phpmyadmin environment.
SET #fieldid := 1234;
SELECT ... WHERE fieldid = #fieldid ...
Or else you can use ? as a query parameter placeholder for the fieldid and bind it to a PHP variable.
See http://php.net/manual/en/mysqli-stmt.bind-param.php
Does the following query do what you want, without using the #fieldid session variable?
SELECT x.fieldid, x.var_name, x.field, x.type, x.groupid, COUNT(*) AS total,
CAST(GROUP_CONCAT(CONCAT(x.value,'(',x.qty,')') SEPARATOR ', ') AS CHAR) a
FROM (
SELECT v.value, v.fieldid, f.var_name, f.field, f.type, g.groupid, COUNT(*) AS qty
FROM extra_field_values AS v
INNER JOIN extra_fields AS f ON f.fieldid=v.fieldid
INNER JOIN extra_fields_groups_item AS g ON g.itemid=v.fieldid
WHERE f.module = 'listings'
AND f.itemid IN (7,8,10,12,15,16,17,18,19,20,21,22,23,24,25,26)
AND f.settings LIKE '%"search";s:1:"1"%'
GROUP BY v.value, v.fieldid
) as x
GROUP BY x.fieldid
I have a mysql table with items in relation to their order.
CREATE DATABASE IF NOT EXISTS `sqltest`;
USE `sqltest`;
DROP TABLE IF EXISTS `testdata`;
CREATE TABLE `testdata` (
`orderID` varchar(10) DEFAULT NULL,
`itemID` varchar(10) DEFAULT NULL,
`qtyOrdered` int(10) DEFAULT NULL,
`sellingPrice` decimal(10,2) DEFAULT NULL
)
INSERT INTO `testdata`(`orderID`,`itemID`,`qtyOrdered`,`sellingPrice`)
values ('1','a',1,'7.00'),('1','b',2,'8.00'),('1','c',3,'3.00'),('2','a',1,'7.00'),('2','c',4,'3.00');
Intended Result:
A = (1+1)2
B = 2
C = (2+4)6 <- most popular
How do I add up all the qty's for each item and result the highest one?
It should be fairly strait forward but I'm new to SQL and I can't work this one out :S
Solution needs to be mysql and or php.
I guess there needs to be some sort of temporary tally variable for each item ID,
but that seems like it could get messy with too many items.
ANSWER:
(thanks nuqqsa)
SELECT itemID, SUM(qtyOrdered) AS total FROM testdata GROUP BY itemID ORDER BY total DESC LIMIT 1;
How about this:
SELECT itemID, SUM(qtyOrdered) AS total FROM testdata GROUP BY itemID ORDER BY total DESC;
SELECT itemID, SUM(qtyOrdered) as blah FROM sqltest GROUP BY itemID ORDER BY blah DESC should do it
SELECT *
FROM testdata
ORDER BY SUM(gtyOrdered) DESC
GROUP BY itemID
SELECT SUM( qtyOrdered ) AS sum_ordered, itemID
FROM testdata
GROUP BY itemID
ORDER BY sum_ordered
select count(qtyOrdered), qtyOrdered from testdata group by qtyOrdered