Using derived subquery result in an other query - mysql

I want to use first subquery result in next select. But it does not see row1 as it is not a table in database. How can I solve this problem?
UPDATE transactions, (SELECT
card_id,
id,
card_sum_before,
card_sum_after,
amount,
dt
FROM transactions
WHERE status = 1 AND
card_id = 1
ORDER BY dt ASC) AS counted_table,
(SELECT
row2.id AS id,
(row1.card_sum_after - row2.amount - row2.card_sum_after) AS difference
FROM counted_table AS row1
INNER JOIN counted_table AS row2 ON row2.id = row1.id + 1
ORDER BY row1.dt ASC) selected
SET transactions.difference = selected.difference
WHERE transactions.id = selected.id`

Please do something like, Store first query output to temp table and then use that temp table in any query you want.

Related

run a query if a value on the last row is correct

Id like to run a query only if a value in the last row is correct. In my exemple if the value in ColumnA is 1 on the last row then i want to run MyQuery. But if the value is not 1 stop there and do nothing.
i've try with case and count(*) and also with If exists. but i keep getting error
SELECT CASE WHEN ((SELECT COUNT(*) FROM
(Select a.* from table as a order by a.index desc limit 1) as b
where b.ColumnA = 1)) > 0 )
THEN (MyQuery)
END
i've also try with if exists but it doesn'work either
if exists Select b.* from (Select a.* from table as a order by a.index desc limit 1) where b.ColumnA = 1
begin
(MyQuery)
end
can you point me what wrong in those query or maybee there's a better way to achive this.
EDIT. This query will be run on a trigger after each insert in that table the goal is to avoid running MyQuery on row that dont required it. MyQuery is a bit slow and most row dont required it to run.
I think we can rephrase your logic here to make it work as you want:
WITH cte AS (
SELECT ColumnA, ROW_NUMBER() OVER (ORDER BY index DESC) rn
FROM yourTable
)
(your query here)
WHERE (SELECT ColumnA FROM cte WHERE rn = 1) = 1;
The WHERE clause above would return either true or false, and would apply to all records in the potential result set from your query. That is, if the ColumnA value from the "last" record were 1, then you would get back the entire result set, otherwise it would be empty set.
Assuming your version of MariaDB supports neither ROW_NUMBER nor CTEs, then use:
(your query here)
WHERE (SELECT ColumnA FROM yourTable ORDER BY index DESC LIMIT 1) = 1;
It depends on what your query is.
INSERT ...
SELECT ... WHERE ... -- this could lead to zero rows being inserted
DELETE ...
WHERE NOT EXISTS ( SELECT ... ) -- this could lead to zero rows being deleted
UPDATE t1 JOIN t2 ... -- the JOIN may cause no rows to be updated
Note:
(Select a.* from table as a order by a.index desc limit 1) as b
where b.ColumnA = 1)) > 0 )
can be simplified (and sped up) to
( ( SELECT ColumnA FROM table ORDER BY index DESC LIMIT 1 ) = 1 )
Note that that is a true/false "expression", so it can be used in various places.

MySQL: create new table using same table twice

I want to use 1 table to create a new table using 2 sets of queries.
To test out the code: http://sqlfiddle.com/#!9/02e3ff/5
Reference table:
Desired table:
They share the same order_id.
type = A, updated_at = pDate
type = B, updated_at = dDate
Query 1:
select t.order_id, t.updated_at as pDate, weekday(t.updated_at) from transactions t
where t.type = 'A' group by t.order_id
Query 2:
select t.order_id, max(t.updated_at) as dDate, weekday(max(t.updated_at)) from transactions t
where t.type= 'B'
group by t.order_id;
For type = A, I want to get the earliest updated_at date, while for type = B, I want to get the latest updated_at date.
Currently, I tried union but they give me 2 rows instead of the desired table.
How do I join or union these 2 queries to get the desired table?
Alternatively, is there a better method to do this? Thanks!
You can try something like this:
SELECT order_id, min(pDate) pDate, max(dDate) dDate FROM(
SELECT
order_id,
if(type='A',updated_at,null) pDate,
if(type='B',updated_at,null) dDate
FROM transactions
) as d
GROUP BY order_id
SQLFiddle

update row if count(*) > n

my DB has this structure:
ID | text | time | valid
This is my current code. I'm trying to find a way to do this as one query.
rows = select * from table where ID=x order by time desc;
n=0;
foreach rows{
if(n > 3){
update table set valid = -1 where rows[n];
}
n++
}
I'm checking how many rows exist for a given ID. Then I need to set valid=-1 for all rows where n >3;
Is there a way to do this with one query?
You can use a subquery in the WHERE clause, like this:
UPDATE table
SET valid=-1
WHERE (
SELECT COUNT(*)
FROM table tt
WHERE tt.time > table.time
AND tt.ID = table.ID
) > 3
The subquery counts the rows with the same ID and a later time. This count will be three or less for the three latest rows; the remaining ones would have a greater count, so their valid field would be updated.
Assuming that (id,time) has a UNIQUE constraint, i.e. no two rows have the same id and same time:
UPDATE
tableX AS tu
JOIN
( SELECT time
FROM tableX
WHERE id = #X -- the given ID
ORDER BY time DESC
LIMIT 1 OFFSET 2
) AS t3
ON tu.id = #X -- given ID again
AND tu.time < t3.time
SET
tu.valid = -1 ;
update table
set valid = -1
where id in (select id
from table
where id = GIVEN_ID
group by id
having count(1) >3)
Update: I really like dasblinkenlight's solution because is very neat, but I wanted to try also to do it in my way, a quite verbose one:
update Table1
set valid = -1
where (id, time) in (select id,
time
from (select id,time
from table1
where id in (select id
from table1
group by id
having count(1) >3)
-- and id = GIVEN_ID
order by time
limit 3, 10000000)
t);
Also in SQLFiddle
to do it for all ids, or only for one if you set a where in the a subquery
UPDATE TABLE
LEFT JOIN (
SELECT *
FROM (
SELECT #rn:=if(#prv=id, #rn+1, 1) AS rId,
#prv:=id AS id,
TABLE.*
FROM TABLE
JOIN ( SELECT #prv:=0, #rn:=0 ) tmp
ORDER BY id, TIMESTAMP
) a
WHERE rid > 3
) ordered ON ordered.id = TABLE.id
AND ordered.TIMESTAMP = TABLE.TIMESTAMP
AND ordered.text = TIMESTAMP.text
SET VALID = -1
WHERE rid IS NOT NULL

Select inside math function

This is most likely a beginner's question in SQL. Is it possible to use a select within a math expression?
For example, I have two tables:
- table A with a column named id (primary key) and another column named val_A
- table B with a column named id (primary key) and another column named val_B
I want to do something like:
select ((select val_A from A where id = 1) +
(select val_B from B where id = 1)) as final_sum;
I'm using MySQL and it is throwing errors. I'm assuming that this is because the result of a select is a set and I want the numeric value of val_A and val_B to be make the sum.
Is there any way of doing this?
Thanks!
The query that you have:
select ((select val_A from A where id = 1) +
(select val_B from B where id = 1)
) as final_sum
is correctly formed SQL in MySQL (assuming that the table and columns exist).
However, it assumes that each subquery only returns one row. If not, you can force it using limit or a function like min() or max():
select ((select val_A from A where id = 1 limit 1) +
(select max(val_B) from B where id = 1)
) as final_sum
Or, possibly, you are trying to get the sum of all the rows with id = 1 in both tables:
select ((select sum(val_A) from A where id = 1) +
(select sum(val_B) from B where id = 1)
) as final_sum
Yes you can do that, but a more proper query format would be:
SELECT (a.val_a + b.val_b) as final_sum
FROM a INNER JOIN b ON a.id = b.id
WHERE a.id = 1
I'm not sure why it's not working, but you could try something like:
select (val_A + val_B) as final_sum from A,B where A.id=1 and B.id=1;
Break down and test your query
select 1+1
so your statement is just without the select. This would run -
select ((select sum(val_A) from A where id = 1) +
(select sum(val_B) from B where id = 1)) as final_sum;

select 2nd row of every ID in mysql

I have a table :
ID | time
1 | 300
1 | 100
1 | 200
2 | 200
2 | 500
I want to get 2nd row for every ID
I know that I can get 1st row as
select ID,time from T group by ID;
But I don't know about how to get 2nd row for every ID.
I know about limit and offset clause in mysql, but can't figure out how to use them here.
How can I do it ?
EDIT : Actually, time is not ordered. I forgot to specify that. I have made an edit in the table.
i have just an idee how to make it but i couldnt fix it , maybe you can fix it. any suggest is appreciated to correct my query
first this to select the first row of each id.
SELECT min(id) id
FROM TableName t2
group by id
then select the min(id) which are not in the first query to select to min(id) (which is second row)
like that
SELECT min(id) id ,time
FROM TableName
WHERE id NOT IN (
SELECT min(id) id
FROM TableName
GROUP BY id
)
GROUP BY id
** as i said its just suggest . it returns me 0 values.if u fix it let me edit my post to be helpful
here a demo
SELECT ID, MAX(time) time
FROM
(
select ID, Time
from TableName a
where
(
select count(*)
from TableName as f
where f.ID = a.ID and f.time <= a.time
) <= 2
) s
GROUP BY ID
SQLFiddle Demo
SELECT x.*
FROM test x
JOIN test y
ON y.id = x.id
AND y.time >= x.time
GROUP
BY id,time
HAVING COUNT(*) = n;
Note that any entries with less than n results will be omitted
You cannot do this with the tables that you have. You could make a valiant attempt with:
select id, time
from (select id, time
from t
group by t
) t
where not exists (select 1 from t t2 where t2.id = t.id and t2.time = t.time)
group by id
That is, attempt to filter out the first row.
The reason this is not possible is because tables are inherently unordered, so there is not real definition of "second" in your tables. This gives the SQL engine the opportunity to rearrange the rows as it sees fit during processing -- which can result in great performance gains.
Even the construct that you are using:
select id, time
from t
group by id
is not guaranteed to return time from the first row. This is a (mis)feature of MySQL called Hidden Columns. It is really only intended for the case where all the values are the same. I will admit that in practice it seems to get the value from the first row, but you cannot guarantee that.
Probably your best solution is to select the data into a new table that has an auto-incrementing column:
create table newtable (
autoid int auto_increment,
id int,
time int
);
insert into newtable(id, time)
select id, time from t;
In practice, this will probably keep the same order as the original table, and you can then use the autoid to get the second row. I want to emphasize, though, the "in practice". There is no guarantee that the values are in the correct order, but they probably will be.