MySQL query with WHERE subquery does not work on no result - mysql

I have written a complex query in MySQL to return a result.
it works perfectly
Until .....
the subquery returns no result
how to write an if or IF null then substitute in date '2020-06-03'
any help greatly appreciated
SELECT
*
FROM
trades
WHERE
stock_code = 'IHVV'
AND acc_id = '4'
AND tx_date >
(SELECT tx_date
FROM
( SELECT *, ( #sum_units := #sum_units + units ) AS sum_units
FROM
trades
JOIN ( SELECT #sum_units := 0 ) params
WHERE
stock_code = 'IHVV'
AND acc_id = '4'
AND tx_date <= '2021-06-30'
AND ( transfer_date IS NULL OR transfer_date <= '2021-06-30' )
ORDER BY
tx_date ASC,
units ASC
) AS query1
WHERE
tx_date < DATE_SUB( '2021-06-30', INTERVAL 1 YEAR )
AND sum_units = 0
ORDER BY
tx_date DESC
LIMIT 1
)
AND tx_date <= '2021-06-30'
AND ( transfer_date IS NULL OR transfer_date <= '2021-06-30' )
ORDER BY
tx_date ASC,
units ASC
clarification
I have written a main query and in one of the where clauses I am using a subquery and this subquery works well, until this subquery does not return a result and my main query stops working, so I would like to on no result in this subquery substitute a value on no result so the main query can function normally
example needed
select * from table where date > (ifnull(subquery, "2002-01-01"))
I get
incorrect parameter count in the call to native function "IFNULL'

AND THE ANSWER IS: ---> :)
thanks guys for all your suggestions
much appreciated
SELECT *
FROM trades
WHERE stock_code = 'IHVV'
AND acc_id = '4'
AND tx_date >
#last 0 date out of 1 year perhaps change the date range by from date and to date in the interval 1 year area
(SELECT COALESCE(
(SELECT tx_date
FROM (SELECT *, (#sum_units := #sum_units + units) AS sum_units
FROM trades
JOIN ( SELECT #sum_units := 0 ) params
WHERE stock_code = 'ANZ'
AND acc_id = '4'
AND tx_date <= '2022-06-30'
AND (transfer_date IS NULL OR transfer_date <= '2022-06-30' ))
ORDER BY tx_date ASC, units ASC) as query1
WHERE tx_date < DATE_SUB('2022-06-30',INTERVAL 1 YEAR)
AND sum_units = 0
ORDER BY tx_date DESC
LIMIT 1), 'TODATE')
AND tx_date <= '2022-06-30'
AND (transfer_date IS NULL OR transfer_date <= '2022-06-30' )
ORDER BY tx_date ASC, units ASC

Related

How to get previous/next row when ordering by date/time then ID in MySQL?

I have table like
SELECT id, name, date FROM `table` ORDER BY `date` DESC, id DESC
...
10 |a|2020-01-08 20:40:00
9 |b|2020-01-08 20:40:00
8 |c|2020-01-08 20:40:00
500 |d|2020-01-06 22:49:00
7 |e|2020-01-06 22:00:00
...
How to get next and previous of a record. (ex: i have info of a record with id = 8 then how to get a next record is 9 and a previous record is 500)
Method #1 (requires MySQL 8+):
SQL
-- Previous ID
WITH cte_desc AS (SELECT * FROM `table` ORDER BY `date` DESC, id DESC),
cte_r AS (SELECT * FROM `table` WHERE id = #r_id)
SELECT id AS prev_id
FROM cte_desc
WHERE `date` < (SELECT `date` FROM cte_r)
OR `date` = (SELECT `date` FROM cte_r) AND id < (SELECT id FROM cte_r)
LIMIT 1;
-- Next ID
WITH cte_asc AS (SELECT * FROM `table` ORDER BY `date`, id),
cte_r AS (SELECT * FROM `table` WHERE id = #r_id)
SELECT id AS next_id
FROM cte_asc
WHERE `date` > (SELECT `date` FROM cte_r)
OR `date` = (SELECT `date` FROM cte_r) AND id > (SELECT id FROM cte_r)
LIMIT 1;
where #r_id is set to the ID of the row you want to find the previous/next for = 8 in your example.
Explanation
Two Common Table Expressions are defined: cte_desc sorts the table and cte_r gets the current row. The query part then finds the top row for which either the date value is strictly less than that of the chosen row or for which it is equal but the id is strictly less.
Online Demo
Dbfiddle demo: https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=5380e374f24243d578db28b9f89b9c8c
Method #2 (for earlier MySQL versions)
Similar to above - just slightly longer when there is no CTE support:
SQL
-- Previous ID
SELECT id AS prev_id
FROM (SELECT * FROM `table` ORDER BY `date` DESC, id DESC) sub
WHERE `date` < (SELECT `date` FROM `table` WHERE id = #r_id)
OR `date` = (SELECT `date` FROM `table` WHERE id = #r_id)
AND id < (SELECT id FROM `table` WHERE id = #r_id)
LIMIT 1;
-- Next ID
SELECT id AS next_id
FROM (SELECT * FROM `table` ORDER BY `date`, id) sub
WHERE `date` > (SELECT `date` FROM `table` WHERE id = #r_id)
OR `date` = (SELECT `date` FROM `table` WHERE id = #r_id)
AND id > (SELECT id FROM `table` WHERE id = #r_id)
LIMIT 1;
Online Demo
Rextester demo: https://rextester.com/MTW78358
Method #3 (Slower? See first comments):
-- Previous ID
SELECT id AS prev_id
FROM `table`
WHERE CONCAT(`date`, LPAD(id, 8, '0')) =
(SELECT MAX(CONCAT(`date`, LPAD(id, 8, '0')))
FROM `table`
WHERE CONCAT(`date`, LPAD(id, 8, '0')) < (SELECT CONCAT(`date`, LPAD(id, 8, '0'))
FROM `table`
WHERE id = #r_id));
-- Next ID
SELECT id AS next_id
FROM `table`
WHERE CONCAT(`date`, LPAD(id, 8, '0')) =
(SELECT MIN(CONCAT(`date`, LPAD(id, 8, '0')))
FROM `table`
WHERE CONCAT(`date`, LPAD(id, 8, '0')) > (SELECT CONCAT(`date`, LPAD(id, 8, '0'))
FROM `table`
WHERE id = #r_id));
Online Demo
Rextester demo: https://rextester.com/BSQQL24519
Explanation
The ordering is by date/time then by ID so to simplify the searching, these are concatenated into a single string - but there is the usual snag of a string ordering placing e.g. 10 after 1 rather than after 9. To overcome this, the IDs are padded with zeros up to the number of digits of the maximum integer in MySQL (4294967295) - using the LPAD function. Having done this groundwork, the previous row can then be found by looking for the largest one that is less than the one for the current id value using MAX and a subselect.
You will need to find first of all, current record's position available in the current ordered list, then you will be able to find the previous record as well as next record.
PREVIOUS RECORD
SELECT row_number, id,name,`date`
FROM (
SELECT #row := #row + 1 AS row_number, id,name,`date`
FROM `table` AS t
JOIN (SELECT #row := 0) r
ORDER BY `date` DESC, id DESC
) main
WHERE row_number = (
SELECT current_row - 1
FROM (
SELECT #curRow := #curRow + 1 AS current_row, t.id,t.name,t.`date`
FROM `table` AS t
JOIN (SELECT #curRow := 0) r
ORDER BY `date` DESC, id DESC
) t1
WHERE id = 8
);
NEXT RECORD
SELECT row_number, id,name,`date`
FROM (
SELECT #row := #row + 1 AS row_number, id,name,`date`
FROM `table` AS t
JOIN (SELECT #row := 0) r
ORDER BY `date` DESC, id DESC
) main
WHERE row_number = (
SELECT current_row + 1
FROM (
SELECT #curRow := #curRow + 1 AS current_row, t.id,t.name,t.`date`
FROM `table` AS t
JOIN (SELECT #curRow := 0) r
ORDER BY `date` DESC, id DESC
) t1
WHERE id = 8
);
Try this query:
SELECT MAX(a.id) AS id
FROM mytable a
JOIN (SELECT id,NAME,DATE FROM mytable WHERE id=8) b
ON a.id <> b.id AND a.date < b.date
UNION ALL
SELECT MIN(a.id) AS id
FROM mytable a
JOIN (SELECT id,NAME,DATE FROM mytable WHERE id=8) b
ON a.id > b.id AND a.date >= b.date;
Fiddle here : https://www.db-fiddle.com/f/gTv6Hyeq9opHW83r6Cxfck/4
Or you can use a variable to single define the value:
SET #val = 8;
SELECT MAX(a.id) AS id
FROM mytable a
JOIN (SELECT id,NAME,DATE FROM mytable WHERE id=#val) b
ON a.id <> b.id AND a.date < b.date
UNION ALL
SELECT MIN(a.id) AS id
FROM mytable a
JOIN (SELECT id,NAME,DATE FROM mytable WHERE id=#val) b
ON a.id > b.id AND a.date >= b.date;
if you know date value of record with id=8 you can query next:
SELECT * FROM table
WHERE id <> 8 AND date >= '2020-01-08 20:40:00'
ORDER BY `date` ASC, id ASC
LIMIT 1
and for previous one(inversed):
SELECT * FROM table
WHERE id <> 8 AND date <= '2020-01-08 20:40:00'
ORDER BY `date` DESC, id DESC
LIMIT 1
if you wish to get both with single query, you can use UNION: https://dev.mysql.com/doc/refman/8.0/en/union.html
If you have at least MySQL 8.0 you could use something like this:
SET #row_number = 0;
SET #target = 8;
WITH cte AS (
SELECT (#row_number:=#row_number + 1) AS num,
t.*
FROM (SELECT *
FROM table_name
ORDER BY date DESC) t
)
SELECT *
FROM cte
WHERE num BETWEEN (SELECT num-1 FROM cte WHERE id = #target)
AND (SELECT num+1 FROM cte WHERE id = #target);
The CTE gives you a row number ordered by the date column. The second part of the query pulls everything from teh CTE that has a row number within 1 of the target row you specified (in this case row id 8).
MySQL 8.0+ is required for CTEs. IF you do not have at least MySQL 8.0 you would have to use temp tables for this method.
Previous:
SELECT id, name, date FROM `table` ORDER BY `date` DESC, id DESC LIMIT 1
Next:
SELECT id, name, date FROM `table` ORDER BY `date` ASC, id ASC LIMIT 1
If you need both in a single query:
( SELECT id, name, date FROM `table` ORDER BY `date` DESC, id DESC LIMIT 1 )
UNION ALL
( SELECT id, name, date FROM `table` ORDER BY `date` ASC, id ASC LIMIT 1 )
If you are using 8.0, you can try this:
SELECT LEAD(id) OVER (ORDER BY id DESC) AS 'Next',id,LAG(id) OVER (ORDER BY id DESC) AS 'Previous'
FROM table
WHERE id = 8
ORDER BY `date` DESC, id DESC

MySQL duration for 9 rows, that's my code

SELECT DATE_FORMAT(b.checktime,"%Y-%m-%d") AS dtDate,
case when (
SELECT DATE_FORMAT(a.checktime,'%H:%i:%s')
FROM checkinout a
WHERE a.checktype='0'
and a.userid=1
and DATE_FORMAT(a.checktime,'%Y-%m-%d')=DATE_FORMAT(b.checktime,'%Y-%m-%d')
LIMIT 1)
is not NULL
then
(
SELECT
DATE_FORMAT(a.checktime,'%H:%i:%s')
FROM checkinout a
WHERE a.checktype='0'
and a.userid=1
and DATE_FORMAT(a.checktime,'%Y-%m-%d')=DATE_FORMAT(b.checktime,'%Y-%m-%d')
LIMIT 1
)
else 'N/A' END
AS Chkin
from checkinout b
where
DATE_FORMAT(b.checktime,"%m")='06' AND
DATE_FORMAT(b.checktime,"%Y")='2019' AND
DATE_FORMAT(b.checktime,"%w")!='0' AND .
DATE_FORMAT(b.checktime,"%w")!='6' AND .
DATE_FORMAT(b.checktime,"%Y-%m-%d") not in
(select date_s from wend)
GROUP BY dtDate
ORDER BY dtDate DESC
Affected rows: 0 Found rows: 9 Warnings: 0 Duration for 1 query: 25.665 sec.
what's wrong with this query, for 9 rows need 25.665 seconds
Thanks
Without understanding the purpose of the query, it can be rewritten to:
SELECT DISTINCT date(b.checktime) AS dtDate,
coalesce((
SELECT time(a.checktime)
FROM checkinout a
WHERE a.checktype = '0'
and a.userid = 1
and date(a.checktime) = date(b.checktime)
LIMIT 1
), 'N/A') AS Chkin
from checkinout b
where month(b.checktime) = 6
AND year(b.checktime) = 2019
AND weekday(b.checktime) not in (5, 6) -- non weekend
AND date(b.checktime) not in (select date_s from wend)
ORDER BY dtDate DESC
Though GROUP BY might perform better than DISTINCT - You should test both. However, the above query can be optimized to use indexes.
The outer condition month(b.checktime) = 6 AND year(b.checktime) = 2019 can be rewritten to b.checktime >= '2019-06-01' AND b.checktime < '2019-06-01' + INTERVAL 1 MONTH. This way the engine should be able to use an index on (checktime).
The condition date(a.checktime) = date(b.checktime) can be written as a.checktime >= date(b.checktime) and a.checktime < date(b.checktime) + INTERVAL 1 DAY. Here I suggest one of the following indexes: (checktype, userid, checktime), (userid, checktype, checktime) or (userid, checktime).
And for date(b.checktime) not in (select date_s from wend) it might be better to use an "anti-join".
So here is the final query, which I would try:
SELECT DISTINCT date(b.checktime) AS dtDate,
coalesce((
SELECT time(a.checktime)
FROM checkinout a
WHERE a.checktype = '0'
and a.userid = 1
and a.checktime >= date(b.checktime)
and a.checktime < date(b.checktime) + INTERVAL 1 DAY
LIMIT 1
), 'N/A') AS Chkin
from checkinout b
LEFT JOIN wend w ON w.date_s = date(b.checktime)
where b.checktime >= '2019-06-01'
AND b.checktime < '2019-06-01' + INTERVAL 1 MONTH
AND weekday(b.checktime) not in (5, 6) -- non weekend
AND w.date_s IS NULL
ORDER BY dtDate DESC

SQL query result should give max count

SELECT count(*) rcount
, DATE(create_date) crdate
, fk_account_id accid
, fk_caa_payment_pool_id poolId
FROM caa_payment_confirmation_hdr
WHERE fk_caa_payment_pool_id IN
(
SELECT pk_caa_payment_pool_id
FROM caa_payment_pool
WHERE is_deleted = 0
)
AND create_date >= '2017-08-01'
GROUP BY crdate
, poolId
I want result to display only max value, please help me out
ORDER the table based on count and then use LIMIT
SELECT * FROM
(SELECT count(*) rcount
, DATE(create_date) crdate
, fk_account_id accid
, fk_caa_payment_pool_id poolId
FROM caa_payment_confirmation_hdr
WHERE fk_caa_payment_pool_id IN
(
SELECT pk_caa_payment_pool_id
FROM caa_payment_pool
WHERE is_deleted = 0
)
AND create_date >= '2017-08-01'
GROUP BY crdate
, poolId
) A ORDER BY cnt DESC LIMIT 1

MySQL exec time black magic

So my mysql query's been loading for 25sec every time. I split query and found out that it works perfectly without one of WHERE conditions. Condition causing problem is :
eshop_products.id IN
(SELECT product-id
FROM eshop_productCombinations
WHERE eshop_productCombinations.recomended = 1
GROUP BY product-id)
Without this condition query took 0.019 sec to load. BUT when I execute this select separately, it takes only 0.026 sec to load:
SELECT product-id
FROM eshop_productCombinations
WHERE eshop_productCombinations.recomended = 1
GROUP BY product-id
Does anyone have any idea what's wrong with my main query? Thank you.
Here's full query (although I don't think it'd be useful for anybody):
SELECT
CAST(
SUBSTRING_INDEX(
GROUP_CONCAT(
price_with_vat ORDER BY IF(eshop_products_cache.`stock` > 0, 1, 0) DESC,
IF(
eshop_products.`type_default_price`=2,eshop_products_cache.`price_with_vat`,
if(
eshop_products.`type_default_price`=0,eshop_products_cache.`default`,null
)
) DESC,
IF(eshop_products.`type_default_price`=1,eshop_products_cache.`price`, null) ASC
),
",
",
1
) AS DECIMAL(10,2)
) AS `price_with_vat`,
SUBSTRING_INDEX(
GROUP_CONCAT(
eshop_products_cache.combination_id ORDER BY IF(eshop_products_cache.`stock` > 0, 1, 0) DESC,
IF(
eshop_products.`type_default_price`=2,
eshop_products_cache.`price_with_vat`,
if(
eshop_products.`type_default_price`=0,
eshop_products_cache.`default`,
null
)
) DESC,
IF(eshop_products.`type_default_price`=1,eshop_products_cache.`price`, null)
ASC
),
",
",
1
) AS `combination_id`,
if( eshop_products.id in ('5993', '6144', '6663', '5120', '5376', '5632', '5888', '6400', '6656', '5121', '5377', '5633'), 1, 0) AS new
FROM `eshop_products` LEFT JOIN `eshop_products_cache` ON eshop_products_cache.product_id=eshop_products.`id` WHERE
(
(
(
eshop_products.stockType = 2 AND eshop_products_cache.stock > 0
)
OR eshop_products.stockType <> 2
)
)
AND
(
price_with_vat > 0
)
AND
(
eshop_products.recomended = 1
OR
eshop_products.id IN (
SELECT `product-id` FROM eshop_productCombinations WHERE eshop_productCombinations.recomended = 1 GROUP BY `product-id`
)
)
AND
(
eshop_products.active = '1'
)
AND (dateStartPublish <= NOW() OR dateStartPublish IS NULL)
AND (dateStopPublish >= NOW() OR dateStopPublish IS NULL)
GROUP BY `eshop_products`.`id`, `eshop_products_cache`.`product_id` ORDER BY RAND() ASC LIMIT 5
Suggested by Anthony , subquery has to be replaced with code below:
EXISTS (
SELECT 1 FROM eshop_productCombinations
WHERE eshop_productCombinations.recomended = 1
AND product-id = eshop_products.id )

mysql date-format returns null

I have the following SQL query. It returns NULL for the 'invoice_date'.
SELECT * , fk_uid AS userId, fk_ivid AS invoice_uid, (
SELECT company
FROM tbl_users
WHERE pk_uid = userId
) AS invoice_customer, (
SELECT DATE_FORMAT( '%d/%m/%y', purchased ) AS invoice_date
FROM _invoices
WHERE invoice_uid = invoice_uid
LIMIT 1
) AS invoice_date
FROM tbl_statement_items
WHERE statement_generated = '1'
AND fk_statementId = '1'
LIMIT 0 , 30
Any help would be appriciated.
Thanks
You have inverted date_format parameters order.