Getting ERROR 1093 (HY000) using subquery - mysql

I have the following table :
--------------------------------------------------------------------------------
| id | pack_id | user_id | start_date | end_date | runtime |
--------------------------------------------------------------------------------
| 1 | 52 | 9 | 2019-04-09 11:53:00 | 2019-04-09 11:54:00 | 60 |
| 2 | 52 | 9 | 2019-04-09 11:58:00 | NULL | NULL |
I would like to update the end_date and runtime of the last id of the same pack_id and user_id.
end_date have to take the current datetime.
Here is my query :
UPDATE
myTable
SET
end_date = NOW(),
runtime = TIMESTAMPDIFF(SECOND, start_date, NOW())
WHERE
id = (
SELECT
max(id)
FROM
myTable
WHERE
user_id = '9'
AND
pack_id = '52'
)
And I get the following error :
ERROR 1093 (HY000) at line 1: You can't specify target table
'myTable' for update in FROM clause

You can't do this in MySQL, so use a JOIN instead:
UPDATE myTable t JOIN
(SELECT max(id) as max_id
FROM myTable
WHERE user_id = '9' AND pack_id = '52'
) tt
ON t.id = tt.max_id
SET t.end_date = NOW(),
t.runtime = TIMESTAMPDIFF(SECOND, t.start_date, NOW()) ;

use subquery deeper into a from clause
UPDATE
myTable
SET
end_date = NOW(),
runtime = TIMESTAMPDIFF(SECOND, start_date, NOW())
WHERE
id = (
select id from (
SELECT
max(id) as id
FROM
myTable
WHERE
user_id = '9'
AND
pack_id = '52'
) a
)

You need a subquery for create a temp set of value and avoid the use of the same table for updated and select at same level.
You can do thi using a nested select eg:
UPDATE myTable
SET end_date = NOW(),
runtime = TIMESTAMPDIFF(SECOND, start_date, NOW())
WHERE id = (
select max_id from (
SELECT
max(id) max_id
FROM myTable
WHERE user_id = '9'
AND pack_id = '52'
) t )
or an inner join on the subquery
UPDATE myTable m
INNER JOIN (
SELECT
max(id) max_id
FROM myTable
WHERE user_id = '9'
AND pack_id = '52'
) t ON t.max_id = m.id
SET end_date = NOW(),
runtime = TIMESTAMPDIFF(SECOND, start_date, NOW())

Related

SQL running total from previous year

How to do running total or cumulative sum from this query? Is it possible to run by correlated subquery? The cumulative sum results will be show as 'New value'.
SELECT
sum(data2.quantity/1000) AS UnitMT
FROM
data2
INNER JOIN itmnocate ON data2.item = itmnocate.ItemNumber
and Source in ('imported','local','by product')
WHERE date1 >= DATE_FORMAT('2018-04-12', '%Y-01-01')- INTERVAL 1 YEAR
AND date1 <= DATE_FORMAT('2018-04-12', '%Y-12-31') - INTERVAL 1 YEAR
AND data2.unit = 'KG'
and data2.customeracc not in (select Customeraccount from custlist WHERE Custcat = 'bcsb')
GROUP BY month(date1)
This is a cumulative problem If your mysql version support window function you can use SUM with window function to do cumulative.
The DATE1 column can be used as the basis for order by to do cumulative.
SELECT *,
sum(UnitMT) over (order by month(date1)) 'New value'
FROM T t1
sqlfiddle
If your mysql version didn't support window function, you can try to use subquery in select to do cumulative.
CREATE TABLE T( date1 DATE,UnitMT int);
INSERT INTO T VALUES ('2017-01-01',66535);
INSERT INTO T VALUES ('2017-02-01',108337);
INSERT INTO T VALUES ('2017-03-01',132767);
INSERT INTO T VALUES ('2017-04-01',100687);
INSERT INTO T VALUES ('2017-05-01',125151);
Query 1:
SELECT *,
(SELECT SUM(UnitMT) FROM T tt WHERE month(tt.date1) <= month(t1.date1)) 'New value'
FROM T t1
Results:
| date1 | UnitMT | New value |
|------------|--------|-----------|
| 2017-01-01 | 66535 | 66535 |
| 2017-02-01 | 108337 | 174872 |
| 2017-03-01 | 132767 | 307639 |
| 2017-04-01 | 100687 | 408326 |
| 2017-05-01 | 125151 | 533477 |
Note
T symbol your current result set data.
You could try this query, it's efficient and works an all MySQL versions:
select #cumSum := 0;
select UnitMT, #cumSum := #cumSum + UnitMT
from tbl
order by date1;
Demo
For your specific problem, you can use variables and a subquery:
SELECT mon, UnitMT,
(#sum := #sum + UnitMT) as running_sum
FROM (SELECT month(date1) as mon, sum(data2.quantity/1000) AS UnitMT
FROM data2 INNER JOIN
itmnocate
ON data2.item = itmnocate.ItemNumber AND
Source IN ('imported', 'local', 'by product')
WHERE date1 >= DATE_FORMAT('2018-04-12', '%Y-01-01') - INTERVAL 1 YEAR AND
date1 <= DATE_FORMAT('2018-04-12', '%Y-12-31') - INTERVAL 1 YEAR AND
data2.unit = 'KG' AND
data2.customeracc not in (select Customeraccount from custlist WHERE Custcat = 'bcsb')
GROUP BY month(date1)
ORDER BY month(date1)
) m CROSS JOIN
(SELECT #sum := 0) params;

UNION query results in a new row instead of new column

The following query returns a result with 5 columns (
date ,lowest_hr_price ,max_hr_price ,min_price , max_price )
instead of
(date ,lowest_hr_price ,max_hr_price , min_price ,max_price , AvgPrice, AvgPieces ).
AvgPrice and AvgPieces are instead added as rows .
(select date(m.min_max_date) as date,
max(case when m.lbl='min_hr_price' then m.min_max_hr_price else null end) as lowest_hr_price,
max(case when m.lbl='max_hr_price' then m.min_max_hr_price else null end) as max_hr_price,
max(case when n.lbl='min_price' then n.min_max_price else null end) as min_price,
max(case when n.lbl='max_price' then n.min_max_price else null end) as max_price
from (select 'min_hr_price' as lbl, price as min_max_hr_price, date_time as min_max_date
from mytable
where date_time in (select min(date_time) as min_date from mytable group by date(date_time)) and symbol = 'dollar'
UNION
select 'max_hr_price', price, date_time
from mytable WHERE symbol = 'dollar'
AND date_time in (select max(date_time) as max_date from mytable WHERE symbol = 'dollar' group by date(date_time))) as m,
(
select 'min_price' as lbl,
min(date_time) as min_max_date,
min(price) as min_max_price
from mytable
WHERE symbol = 'dollar'
group by date(date_time)
UNION
select 'max_price' as lbl,
max(date_time) as min_max_date,
max(price) as min_max_price
from mytable
WHERE symbol = 'dollar'
group by date(date_time)
) n
where m.min_max_date=n.min_max_date
group by date(m.min_max_date)
order by m.min_max_date DESC
)
UNION
(SELECT null, null, date_time, avg (price) as AvgPrice, avg (pieces) as AvgPieces FROM mytable
WHERE symbol = 'dollar'
group by date(date_time))
Actual result:
date |lowest_hr_price | max_hr_price | min_price | max_price
------------------------------------------------------------------------------------
2018-03-06 | 1 | 2 | 0 | 10
NULL | NULL | {date} | {avgprice} | {avgpieces}
Expected result:
date |lowest_hr_price | max_hr_price | min_price | max_price | AvgPrice | AvgPieces
-------------------------------------------------------------------------------------------------------------
2018-03-06 | 1 | 2 | 0 | 10 | {avgprice}| {avgpieces}
if you instead of an union (select rows append one select to each others ) need all the result on the same rows
in this case you could use a cross join eg :
select distinct * from (
(select date(m.min_max_date) as date,
max(case when m.lbl='min_hr_price' then m.min_max_hr_price else null end) as lowest_hr_price,
max(case when m.lbl='max_hr_price' then m.min_max_hr_price else null end) as max_hr_price,
max(case when n.lbl='min_price' then n.min_max_price else null end) as min_price,
max(case when n.lbl='max_price' then n.min_max_price else null end) as max_price
from (select 'min_hr_price' as lbl, price as min_max_hr_price, date_time as min_max_date
from mytable
where date_time in (select min(date_time) as min_date from mytable group by date(date_time)) and symbol = 'dollar'
UNION
select 'max_hr_price', price, date_time
from mytable WHERE symbol = 'dollar'
AND date_time in (select max(date_time) as max_date from mytable WHERE symbol = 'dollar' group by date(date_time))) as m,
(
select 'min_price' as lbl,
min(date_time) as min_max_date,
min(price) as min_max_price
from mytable
WHERE symbol = 'dollar'
group by date(date_time)
UNION
select 'max_price' as lbl,
max(date_time) as min_max_date,
max(price) as min_max_price
from mytable
WHERE symbol = 'dollar'
group by date(date_time)
) n
where m.min_max_date=n.min_max_date
group by date(m.min_max_date)
order by m.min_max_date DESC
) ) T1 INNER join
(SELECT null, null, date_time, avg (price) as AvgPrice, avg (pieces) as AvgPieces FROM mytable
WHERE symbol = 'dollar'
group by date(date_time)) T2 ON date(T1.date) = date(T2.date_time)

Using IFNULL in where clause

I have a problem with an SQL statement not returning any result from a specific result.
SELECT statementbalance AS 'BringForwardFromPreviousDay',
IFNULL((SELECT SUM(statementdebit) FROM statement WHERE merchantid = '4' AND statementdate = '2018-01-08'),0) AS 'TotalDebit',
IFNULL((SELECT SUM(statementcredit) FROM statement WHERE merchantid = '4' AND statementdate = '2018-01-08'),0) AS 'TotalCredit',
IFNULL((SELECT statementbalance FROM statement WHERE merchantid = '4' AND statementdate <= '2018-01-08' ORDER BY transactionid DESC LIMIT 1),0) AS 'TotalBalance'
FROM statement
WHERE merchantid = '4' and statementdate <= '2018-01-07'
ORDER BY transactionid DESC LIMIT 1
The SQL statement is to capture data dated from today '2018-01-08'and from previous days '2018-01-07' and grab the last recorded data hence the '<='
Because merchantid = '4' is a newly added merchant, it does not have any data to grab from <= 2018-01-07 hence i want it to return 0 instead of null and prevent other data from returning null.
I tried adding ifnull on the statementnbalance but it still returns null and i can only think of including ifnull on the where clause but i tried to no avail.
Here is the sqlfiddle of using merchantid '2' that works fine.
http://sqlfiddle.com/#!9/7cae3e0/1
I think this is what you want:
select
x.merchantid as merchantid,
ifnull(c.previous_days_balance, 0) as BringForwardFormPreviousDay,
ifnull(a.latest_total_debit, 0) as TotalDebit,
ifnull(a.latest_total_credit, 0) as TotalCredit,
ifnull(b.latest_balance, 0) as LatestBalance
from
(
select distinct merchantid from statement
) x
left outer join
(
select
merchantid,
sum(statementdebit) as latest_total_debit,
sum(statementcredit) as latest_total_credit
from
statement
where
statementdate = '2018-01-08'
group by
merchantid
) a
on x.merchantid = a.merchantid
left outer join
(
select
merchantid,
statementbalance as latest_balance
from
statement
where
(merchantid, transactionid) in
(
select
merchantid,
max(transactionid)
from
statement
where
statementdate = '2018-01-08'
group by
merchantid
)
) b
on x.merchantid = b.merchantid
left outer join
(
select
merchantid,
statementbalance as previous_days_balance
from
statement
where
(merchantid, transactionid) in
(
select
merchantid,
max(transactionid)
from
statement
where
statementdate <= '2018-01-07'
group by
merchantid
)
) c
on x.merchantid = c.merchantid;
I added another row to illustrate the extra case:
INSERT INTO statement VALUES ('99', '5', '131', 'Purchase: TopUp Cheezy', '2018-01-05', '23:35:31', '38.20', '0.00', '5000.00');
The results are:
+------------+-----------------------------+------------+-------------+----------------+
| merchantid | BringForwardFormPreviousDay | TotalDebit | TotalCredit | LatestBalance |
+------------+-----------------------------+------------+-------------+----------------+
| 1 | 35 | 15 | 0 | 5 |
| 2 | 182.33 | 4.9 | 0 | 177.43 |
| 4 | 0 | 95.48 | 200 | 104.52 |
| 5 | 5000 | 0 | 0 | 0 |
+------------+-----------------------------+------------+-------------+----------------+
4 rows in set (0.00 sec)
This assumes that transactionId keeps on increasing with time. That is not an entirely safe assumption. It would be better to use timestamps rather than dates for the transaction so you can find the latest one (or the latest one that is before today). I see that you do have statementtime but a separate column...
The query doesn't return anything because there are no data to return that fit your where clause. What you could do is using an IFNULL on your whole select statement:
SELECT ifnull((SELECT statementbalance AS 'BringForwardFromPreviousDay'
FROM statement
WHERE merchantid = '4'
AND statementdate <= '2018-01-07'
ORDER BY transactionid DESC LIMIT 1), 0)
and work from there. However, your statement can only contain one column, so you either have to work this into a stored procedure, or you have to deal with this issue at a different place (maybe in your code?)
You need to use a LEFT JOIN to allow for rows that don't exist.
SELECT IFNULL(t3.statementbalance, 0) AS BringForwardFromPreviousDay,
t1.TotalDebit, t1.TotalCredit, t2.TotalBalance
FROM (SELECT SUM(statementdebit) AS TotalDebit,
SUM(statementcredit) AS TotalCredit
FROM statement
WHERE merchantId = '4' AND statementdate = '2018-01-08') AS t1
CROSS JOIN (
SELECT statementbalance AS TotalBalance
FROM statement
WHERE merchantId = '4' AND statementdate <= '2018-01-08'
ORDER BY statementdate DESC
LIMIT 1) AS t2
LEFT JOIN (
SELECT statementbalance
FROM statement
WHERE t3.merchantID = '4' AND t3.statementdate <= '2018-01-07'
ORDER BY statementdate DESC
LIMIT 1) ON 1=1

MySQL Query to return total hours for state information

I have a mysql table capturing state information for a signal every minute in MySQL table as follows:
ID | state | timestamp |
--------------------------------------
'sig1'| 'red' | '2017-07-10 15:30:21'
'sig1'| 'green' | '2017-07-10 15:31:26'
'sig1'| 'green' | '2017-07-10 15:32:24'
'sig1'| 'red' | '2017-07-10 15:33:29'
'sig1'| 'red' | '2017-07-10 15:34:30'
'sig1'| 'red' | '2017-07-10 15:35:15'
I need to come up with a query where it result should be the most recent time 'sig1' was in 'red' state for more than 5 minutes consecutively, the output of the query should be
ID | state| duration | start_time | end_time
So if you guys can help me with the query, that would be great!
cheers!
You can try something like this:
SELECT t.id,t.consecutive,t.state
,COUNT(*) consecutive_count
,MIN(timestamp) start_time
,MAX(timestamp) end_time
,TIMEDIFF(MAX(timestamp), MIN(timestamp)) AS diff /* for ckeck*/
FROM (SELECT a.* ,
#r:= CASE WHEN #g = a.state AND #h=a.id THEN #r ELSE #r + 1 END consecutive,
#g:= a.state g,
#h:= a.id h
FROM yourtable a
CROSS JOIN (SELECT #g:='', #r:=0, #h:='') t1
ORDER BY id
) t
GROUP BY t.id,t.consecutive,t.state
HAVING (UNIX_TIMESTAMP(end_time)-UNIX_TIMESTAMP(start_time))/60>5
;
Sample data:
CREATE TABLE yourtable (
id VARCHAR(10) NOT NULL ,
state VARCHAR(10) NOT NULL,
timestamp datetime
);
INSERT INTO yourtable VALUES ('sig1','red','2017-07-10 15:30:21');
INSERT INTO yourtable VALUES ('sig1','green','2017-07-10 15:31:26');
INSERT INTO yourtable VALUES ('sig1','green','2017-07-10 15:32:24');
INSERT INTO yourtable VALUES ('sig1','red','2017-07-10 15:33:29');
INSERT INTO yourtable VALUES ('sig1','red','2017-07-10 15:34:30');
INSERT INTO yourtable VALUES ('sig1','red','2017-07-10 15:39:15');
INSERT INTO yourtable VALUES ('sig2','red','2017-07-10 15:15:15');
Output:
id consecutive state consecutive_count start_time end_time diff
sig1 3 red 3 10.07.2017 15:33:29 10.07.2017 15:39:15 00:05:46
SELECT TIMESTAMPDIFF(HOUR,MAXTIME ,MINTIME),ID,state FROM
(
SELECT ID,state,MIN(timestamp)MINTIME,MAX(timestamp) MAXTIME FROM TABLE GROUP BY ID,state
)Z
Try above query.

SQL selection of last period

I have a table like this:
<table border="1" cellspacing="0" cellpadding="5">
<tr><td>ID</td><td>status</td><td>start_date</td><td>end_date</td></tr>
<tr><td>1</td><td>A</td><td>2015-01-01</td><td>2015-12-31</td></tr>
<tr><td>1</td><td>B</td><td>2016-01-01</td><td>NULL</td></tr>
<tr><td>2</td><td>C</td><td>NULL</td><td>2016-12-31</td></tr>
<tr><td>3</td><td>D</td><td>NULL</td><td>NULL</td></tr>
</table>
I must do a trigger in MySQL to update a main table with the last status of a subject.
To do this I must select for every ID the last status based on the period between start_date and end_date that can be NULL if didn't known.
I write this SQL statement test:
SELECT *
FROM My_Table AS T
WHERE T.start_date Is Null AND
T.end_date In(SELECT MAX(end_date) FROM My_Table WHERE ID = T.ID)
OR
T.start_date In(SELECT MAX(start_date) FROM My_Table WHERE ID = T.ID) AND
T.end_date Is Null
OR
T.start_date In(SELECT MAX(start_date) FROM My_Table WHERE ID = T.ID) AND
T.end_date In(SELECT MAX(end_date) FROM My_Table WHERE ID = T.ID
);
Is there a shorten method to do this?
In practice I want a WHERE clause that catch each of these cases (a = start_date, b=end_date):
a = MAX AND b = MAX OR
a = NULL AND b = MAX OR
a = MAX AND b = NULL OR
a = NULL AND b = NULL
Here's a slightly simplified query which returns exactly the same result set as the query in your question. It uses only two subqueries instead of four (no duplicated subqueries):
SELECT *
FROM My_Table AS T
WHERE
(T.start_date Is Null OR T.start_date = (SELECT MAX(start_date) FROM My_Table WHERE ID = T.ID)) AND
(T.end_date Is Null OR T.end_date = (SELECT MAX(end_date) FROM My_Table WHERE ID = T.ID)) AND
(T.start_date Is NOT Null OR T.end_date Is NOT Null)
;
Note that it does NOT include the a = NULL AND b = NULL case. To include it as well, the last AND-clause must be removed, i.e.:
SELECT *
FROM My_Table AS T
WHERE
(T.start_date Is Null OR T.start_date = (SELECT MAX(start_date) FROM My_Table WHERE ID = T.ID)) AND
(T.end_date Is Null OR T.end_date = (SELECT MAX(end_date) FROM My_Table WHERE ID = T.ID))
;