I have a query that return something like this:
| ID | Val |
| 0 | 10 |
| 1 | 20 |
| 2 | 30 |
But instead of that, I want something like this:
| ID | Val | Sum |
| 0 | 10 | 10 |
| 1 | 20 | 30 |
| 2 | 30 | 60 |
Is that a way to do it on the query (I'm using MySQL)?
Tks
This is called cumulative sum.
In Oracle and PostgreSQL, it is calculated using a window function:
SELECT id, val, SUM() OVER (ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
FROM mytable
However, MySQL does not support it.
In MySQL, you can calculate it using session variables:
SET #s = 0;
SELECT id, val, #s := #s + val
FROM mytable
ORDER BY
id
;
or in a pure set-based but less efficient way:
SELECT t1.id, t1.val, SUM(t2.val)
FROM mytable t1
JOIN mytable t2
ON t2.id <= t1.id
GROUP BY
t1.id
;
Would something like this work for your purposes? (Warning, potentially really darned slow with the subselect).
SELECT t1.id, t1.val, (SELECT SUM(val) FROM table AS t2 WHERE t2.id <= t1.id) 'sum'
FROM table AS t1
ORDER BY id ASC
Assuming the table name is t, you can use a query like this:
select t.id, t.val, sum(t2.val) Sum
from t, t t2
where t2.id <= t.id
group by t.id, t.val
(tested in Oracle)
Related
I got two tables with identical structure. From those tables I need to get rows with highest value on rate column where fix_id is the same.
Table1
fix_id | rate | proc | unique_id
2 | 72 | 50 | 23_tab1
3 | 98 | 70 | 24_tab1
4 | 78 | 80 | 25_tab1
table2
fix_id | rate | proc | unique_id
2 | 75 | 999 | 23_tab2
3 | 80 | 179 | 24_tab2
4 | 82 | 898 | 25_tab2
Expected result
fix_id | rate | proc | unique_id
2 | 75 | 999 | 23_tab2
3 | 98 | 70 | 24_tab1
4 | 82 | 898 | 25_tab2
I've tried this...
Select fix_id,proc,unique_id,MAX(rate) rate from
(Select fix_id,proc,unique_id,MAX(rate) rate from table1 group by fix_id
UNION ALL SELECT fix_id,proc,unique_id,MAX(rate) rate from table2 group by fix_id ) group by fix_id
I get the highest values from rate column but the values from other columns are incorrect.
It can be done using CASE statement.
Try this query
select
(case
when T1.rate > T2.rate then T1.fix_id else T2.fix_id
end) as fix_id,
(case
when T1.rate > T2.rate then T1.rate else T2.rate
end) as rate,
(case
when T1.rate > T2.rate then T1.proc else T2.proc
end) as proc,
(case
when T1.rate > T2.rate then T1.unique_id else T2.unique_id
end) as unique_id
from table1 as T1, table2 as T2 where T1.id = T2.id
You can use row_number():
select t.*
from (select fix_id, proc, unique_id, rate,
row_number() over (partition by fix_id order by rate desc) as seqnum
from ((select fix_id, proc, unique_id, rate from table1
) union all
(select fix_id, proc, unique_id, rate from table2
)
) t
) t
where seqnum = 1;
As fix_id is unique in both tables, the answer with CASE statements (https://stackoverflow.com/a/65609931/53341) is likely the fastest (so, I've upvoted that)...
Join once
Compare rates, on each row
Pick which table to read from, on each row
For large numbers of columns, however, it's unwieldy to type all the CASE statements. So, here is a shorter version, though it probably takes twice as long to run...
SELECT t1.*
FROM table1 AS t1 INNER JOIN table2 AS t2 ON t1.fix_id = t2.fix_id
WHERE t1.rate >= t2.rate
UNION ALL
SELECT t2.*
FROM table1 AS t1 INNER JOIN table2 AS t2 ON t1.fix_id = t2.fix_id
WHERE t1.rate < t2.rate
ie:
| id | num |
| a | 1 |
| b | 2 |
| c | 3 |
| d | 4 |
| e | 5 |
and this query is essentially what I'm trying to do:
select num as number, sum(case num > number then num else 0 end) as summation from table;
(I'm trying to sum up all the ints larger than the currently selected num in the column num.)
example output from above table:
| num | summation |
| 1 | 14 |
| 2 | 12 |
| 3 | 9 |
| 4 | 5 |
|5 | 0 |
The problem lies in the fact that I can't use the alias defined in the same select statement; is there another way?
Thanks!
If you're on MySQL 8.0 you can use window functions.
SELECT num,
sum(num) OVER (ORDER BY num DESC) - num summation
FROM elbat
ORDER BY num;
Prior to MySQL 8.0 you can use a correlated subquery.
SELECT t1.num,
coalesce((SELECT sum(t2.num)
FROM elbat t2
WHERE t2.num > t1.num),
0) summation
FROM elbat t1
ORDER BY t1.num;
You can write this using a correlated subquery:
select num,
(select sum(num)
from t 2
where t2.num >= t.num
) - num as summation
from t;
You can use correlated subquery :
select num,
(select sum(num)
from table t2
where t2.num > t.num
) as summation
from table t1;
this works, although a bit messy:
select num, (select sum(case when table.num > temp.num then num else 0 end)
from (select * from table) as temp
) as summation
from table;
I am creating the following two temporary tables t1 and t2 using two SELECT statements:
+------+------+
| Col1 | Col2 |
+------+------+
| A | 1 |
| B | 2 |
| C | 3 |
+------+------+
and
+------+------+
| Col3 | Col4 |
+------+------+
| C | 5 |
| D | 6 |
| E | 7 |
+------+------+
The two SELECT statements by nature are always returning the same number of rows. Now I want to join/combine these two results horizontally to get the following output table:
+------+------+------+------+
| Col1 | Col2 | Col3 | Col4 |
+------+------+------+------+
| A | 1 | C | 5 |
| B | 2 | D | 6 |
| C | 3 | E | 7 |
+------+------+------+------+
I tried working with multiple JOIN statement, but could figure out a smart way. I also tried the UNION statement, which delivered a vertical join, but not the required horizontal version.
Here two easy SELECT statement for better orientation in possible solutions:
SELECT * FROM `t1` WHERE date = DATE(NOW())
SELECT * FROM `t2` WHERE date = DATE(NOW())
Thanks in advance for your help.
Try this
SET #row_number_t1:=0;
SET #row_number_t2:=0;
SELECT t1_modif.*, t2_modif.* FROM
(SELECT #row_number_t1:=#row_number_t1+1 AS row_number,
t1.* FROM t1)
t1_modif
JOIN (SELECT #row_number_t2:=#row_number_t2+1 AS row_number,
t2.* FROM t2)
t2_modif ON t2_modif.row_number = t1_modif.row_number
Note that order is not guaranteed, to do this add ORDER BY clause at the end of each FROM t1 and FROM t2 subqueries, basically we are joining by row_number, since MySQL doesn't have ROW_ID, ROW_NUM (similar to mssql, oracle, postgres) we have used session variables
Creating Virtual IDs for relation. This is not a recommended way though.
SELECT col1, col2, col3, col4 FROM
(SELECT t1.*, (#t1VID := #t1VID + 1) t1VID FROM t1 , (SELECT #t1VID := 0) d) e
JOIN (SELECT t2.*, (#t2VID := #t2VID + 1) t2VID FROM t2, (SELECT #t2VID := 0) a ) b ON t1VID = t2VID
JOIN (SELECT #t1VID := 0) c ;
Ideal solution would have been adding proper relation between the tables. If not, it is best to query it separately and do the necessary joining in application layer
lets say I have this table:
| id | record_id | date_updated |
|----|-----------|--------------|
| 1 | 1 | 19-03-2015 |
| 2 | 1 | 18-03-2015 |
| 3 | 1 | 17-03-2014 |
| 4 | 2 | 01-01-2015 |
| 5 | 2 | 05-02-2015 |
so the results I am looking for are :
| id | record_id | date_updated |
|----|-----------|--------------|
| 1 | 1 | 19-03-2015 |
| 4 | 2 | 01-01-2015 |
I have array with record ids.
$records = [1,2];
So I can do something like:
select * from `mytable`
WHERE `record_id` IN ($records)
AND mytable.date_update > 01-01-2014
AND mytable.date_updated < 12-12-2015
so mysql will select records wich match date_updated criteria ( and record id ofc ), which are more then 1 for each record ID, basically I want to make him limit the rows for each $record_id to 1
If it is even possible.
//it is super hard to explain the problem, the real case is that this is a sub query of another query, but the real example is 10 rows query and 100 columns table, so it will be even more hard to explain the situation and for someone to read it / udnerstands it. Hopefully someone will understand my problem, if not I will try to explain more.
Thanks
You can try using the group by clause
SELECT *
FROM `mytable`
WHERE id IN (
SELECT min(id)
FROM `mytable`
WHERE `record_id` IN ($records)
AND mytable.date_update > 01-01-2014
AND mytable.date_updated < 12-12-2015
group by record_id
);
There are many ways to get the record per group, and since you need only once you can easily do as below
select t1.* from table_name t1
where (
select count(*) from table_name t2
where t1.record_id = t2.record_id
) > =0
and
t1.date_updated > '2014-01-01' and date_updated < '2015-12-12'
group by t1.record_id ;
There are other way too using left join
select t1.* from table_name t1
left join table_name t2 on t1.record_id = t2.record_id
and t1.id >t2.id where t2.id is null
This will give you data with asc order with id
If you need data with max(id) for a record_id you can use
t1.id < t2.id
instead of
t1.id >t2.id
The same comparison you can do with first query.
I know that this is a duplicate of Select Rows with Maximum Column Value group by Another Column but I want to select rows that have the maximum column value,as group by another column , but without nested select statement, I know it can be done like this:
SELECT
T.Name,
T.Rank,
T.ID
FROM MyTable T
WHERE T.Rank = (
SELECT MAX( T1.Rank) FROM MyTable T1
WHERE T1.Name= T.Name
)
where ID,
Rank,
Name is the table schema, and I want to group by results by Name first, and then choose one row from each Name group, depending on which one has the highest Rank.
Attached is a sample of the table I want to select from
mysql> SELECT t1.nm, t1.rank,t1.id
FROM mytable t1
LEFT JOIN (
SELECT nm, max(rank) as top
FROM mytable t2
GROUP BY nm
) AS t2 ON t1.nm=t2.nm AND t1.rank = t2.top
WHERE t2.nm IS not NULL
ORDER BY nm;
+----+------+---------+
| nm | rank | id |
+----+------+---------+
| m | -1 | b7kjhsf |
| n | 13 | d3sf |
+----+------+---------+
2 rows in set (0.00 sec)
mysql> select * from mytable;
+----+------+----------+
| nm | rank | id |
+----+------+----------+
| n | 11 | asfd |
| n | 11 | bsf |
| n | 11 | zzasdfsf |
| n | 13 | d3sf |
| n | 11 | effesf |
| n | 10 | yxxgesf |
| n | 11 | bkhjusf |
| m | -1 | b7kjhsf |
| m | -4 | cdfgabsf |
+----+------+----------+
9 rows in set (0.00 sec)
As mentioned in the other answer, the only other alternative that I know of, is using Common Table Expressions:
;WITH CTE AS
(
T.Name,
T.Rank,
T.ID,
ROW_NUMBER() OVER
(PARTITION BY Name ORDER BY Rank DESC)
AS RowNumber
FROM MyTable
)
SELECT *
FROM CTE
WHERE RowNumber = 1
SELECT Name, Id, Rank FROM
(
SELECT T.Name, T.Id, T.Rank, RANK() OVER (PARTITION BY T.Name ORDER BY T.Rank DESC) = 1 AS NameRank
FROM MyTable T
)
WHERE NameRank = 1
Not sure whether you are just trying to exclude the nested select, and whether joining aginst a subselect would be acceptable. If so:-
SELECT
T.Name,
T.Rank,
T.ID
FROM MyTable T
INNER JOIN (SELECT Name, MAX(Rank) AS MaxRank FROM MyTable GROUP BY Name ) T1
ON T.Name = T1.Name
AND T.Rank = T1.MaxRank