I would like to sum values in my table, except the ones that are absolute (field absolute, value = 1). If that's the case, the summing should reset. Example:
Date Value Absolute
1-1-2020 4 0
1-2-2020 7 1
1-3-2020 3 0
Regular SUM() would return (4+7+3=) 14. But in this example it should reset at value 7, which makes a sum of (7+3=) 10.
How can I make this work?
You seem to want a window sum that resets everytime absolute is 1. If so, you can do:
select t.*, sum(value) over(partition by grp order by date) sum_value
from (
select t.*, sum(absolute) over(order by date) grp
from mytable t
) t
The subquery uses a window sum of absolute to define the groups, then the outer query performs the sums value over each group.
Related
I have rows in SQLite, let's say 10.0000 with last row as unix timestamp. I would like to average every value from today 00:00 to 23:59 into X averaged group. If there is 1000 records today and X is 10, then average each 100 value and the result would be 10x averaged 100 records. If x is 20, average each value and result is averaged values 50x. Those values are from sensors, like temperature and I would like to be able to track what the temperature was today between X and Y hours and so, for each day.
What would be the best efficient way to do this? I'm using SQLite3 with C++, I could do it in C++ with more queries but I would like to let this to SQLite and fetch the result only if it's possible. Visualization: https://i.ibb.co/grSTgrZ/sqlite.png
Any help appreciated where I should start with this.
Thanks.
You can use NTILE() window function to create the groups on which you will aggregate:
SELECT AVG(value) avg_value
FROM (
SELECT *, NTILE(3) OVER (ORDER BY id) grp
FROM tablename
)
GROUP BY grp
The number inside the parentheses of NTILE() corresponds to the number X in your requirement.
Id is the column on which the table should be ordered.
If you have a date column then change to:
SELECT AVG(value) avg_value
FROM (
SELECT *, NTILE(3) OVER (ORDER BY date) grp
FROM tablename
)
GROUP BY grp
See a simplified demo.
I have a derived table that looks like this example:
{select * from tb_data}
I want the results to have and additional summation column, the catch is I need the summation column to reset the working value if the info column value = 'reset'
{select *, (I assume some variation on sum(number) over (partition by id order by date desc)) as summation from tb_data}
and here's what the output should look like:
The actual derived table covers thousands of ids which is why it needs to be partitioned by the id and ordered by date desc and each has a different number of reset points.
What SQL query will get me the output I need?
You could first do a conditional window sum to define the groups: everytime a reset is found, a new group starts. Then you can simply do a window sum of numbers within the groups.
select
id,
date,
info,
number,
sum(number) over(partition by id, grp order by date) summation
from (
select
t.*,
sum(case when info = 'reset' then 1 else 0 end)
over(partition by id order by date) grp
from mytable t
) t
I'm trying to compute some queries, with the aggregates functions.
The problem is I'm not able to compute the average of the column, without the first value.
example
_myColumn_
10
15
20
Final average: (10 + 15 + 20) / 3 = 15
What I want is: (15 + 20) / 2 = 12.5
This is the code I've tried without success
select avg(age) from testing
except
select avg(age) from testing
limit 1
First use OFFSET clause to skip the first row. (You should really ensure the order with an ORDER BY clause.) Then compute the AVG on that result:
select avg(age)
from
(
select age from testing
offset 1
) dt
Or, if the first row is expected to be the one with the lowest age:
select (sum(age) - min(age)) / (count(*) - 1)
from testing
There is no such thing as a "first" row in SQL, because tables represent unordered sets. A column is needed to specify the ordering.
Let me assume you mean the row with the smallest value. This is a little tricky, but you can use row_number():
select avg(age)
from (select t.*, row_number() over (order by age) as seqnum
from t
) t
where seqnum > 1;
I'd propose use something like this(some field Should be UNIQUE, for example ID if you have one)
SELECT AVG(age) FROM testing WHERE ID NOT IN
(SELECT ID FROM testing ORDER BY ??SOMETHING HERE?? limit 1)
I need a way to put results into # of groups that I specify.
I have tried ntile() function, which I thought would use but it's not working:
WITH CTE AS (
SELECT 1 as Number
UNION ALL
SELECT Number+1
FROM CTE
WHERE Number < 100
)
SELECT *, ntile(80) over (order by number desc) as 'test'
FROM CTE
For the expected results, the Quartile column should output a number for every 2 entries (as specified in NTILE(80)), but it can be 2, 4, 10, or any number I specify.
Maybe NTILE() is not the right function but is there a function that does what I want?
So, if I specify 3, then the result should group every 3 records. If I specify 15, then the result should group every 15 records and move onto next group.
Hope I'm being clear
...should output a number for every 2 entries...
No, you have 100 entries and you want to divide them in 80 groups. You'll get some groups with 1 entry and other groups with 2 entries.
Read the definition of NTILE(). If you want groups with 2 entries you can do it as shown below by dividing it in 50 groups:
WITH recursive
CTE AS (
SELECT 1 as Number
UNION ALL
SELECT Number + 1
FROM CTE
WHERE Number < 100
)
SELECT *,
ntile(50) -- changed here
over (order by number desc) as test
FROM CTE
You didn't say what database engine you are using, so I assumed PostgreSQL.
I think you simply want the modulus operator:
WITH CTE AS (
SELECT 1 as Number
UNION ALL
SELECT Number+1
FROM CTE
WHERE Number < 100
)
SELECT cte.*,
(ROW_NUMBER() OVER (ORDER BY Number DESC) - 1) % 3 -- or however many groups that you want
FROM CTE
I need to write a query that selects a minimum value and it's second most minimum value from a list of integers.
Grabbing the smallest value is obvious:
select min(value) from table;
But the second smallest is not so obvious.
For the record, this list of integers is not sequential -- the min can be 1000, and the second most min can be 10000.
Use an analytic function
SELECT value
FROM (SELECT value,
dense_rank() over (order by value asc) rnk
FROM table)
WHERE rnk = 2
The analytic functions RANK, DENSE_RANK, and ROW_NUMBER are identical except for how they handle ties. RANK uses a sports-style process of breaking ties so if two rows tie for a rank of 1, the next row has a rank of 3. DENSE_RANK gives both of the rows tied for first place a rank of 1 and then assigns the next row a rank of 2. ROW_NUMBER arbitrarily breaks the tie and gives one of the two rows with the lowest value a rank of 1 and the other a rank of 2.
select
value
from
(select
value,
dense_rank() over (order by value) rank
from
table)
where
rank = 2
Advantage: You can get the third value just as easy, or the bottom 10 rows (rank <= 10).
Note that the performance of this query will benefit from a proper index on 'value'.
SELECT MIN(value)
FROM TABLE
WHERE Value > (SELECT MIN(value) FROM TABLE)