How to merge two rows in SQL Server - sql

I have a table similar to this one:
Date | Cond | Time
---------+--------+------
18/03/19 | 1 | 13:07
18/03/19 | 0 | 16:07
I want to have a selection that would produce thing similar to that using join or union or any sort of condition
Date | Time1 | Time2
----------+-------+------
18/03/19 | 13:07 | 16:07
Best regards

You can use conditional aggregation:
select date, max(case when cond = 1 then time end) as time_1,
max(case when cond = 0 then time end) as time_0
from t
group by date
order by date;

use aggregate function
select date,min(time),max(time)
from table group by date

Related

Get last date with data for each calendar date

I have some data : sales amount for each day, but sometimes I have missing data so no record (for example on the weekend, but not only). For these dates, I want to replace the null value with the last known value. I create a reference table with all calendar dates and a boolean to tell me if I have data for this day.
For example with this reference table :
Date
is_data_present
27/10/2022
1
28/10/2022
1
29/10/2022
0
10/10/2022
0
I want this outcome :
Date
is_data_present
date_to_use
27/10/2022
1
27/10/2022
28/10/2022
1
28/10/2022
29/10/2022
0
28/10/2022
30/10/2022
0
28/10/2022
I tried things with LEAD but I don't know how to add a condition like 'where is_data_present = 1'
Basically, you don't need a window function for this.
The coalsesce is for the case that the first row is 0, and so has no value that is prior to it
SELECT
"Date", "is_data_present",
COALESCE((SELECT "Date" FROM table1 WHERE "Date" <= Tab1."Date" AND "is_data_present" = 1 ORDER BY "Date" DESC LIMIT 1 ),"Date") date_to_use
FROM table1 tab1
I tried things with LEAD but I don't know how to add a condition like 'where is_data_present = 1'
In addtion to #nbk's approach, you might consider FIRST_VALUE or LAST_VALUE if you want to use a window function since LEAD or LAG doesn't support IGNORE NULLS in it.
WITH sample_table AS (
SELECT '27/10/2022' date, 1 is_data_present UNION ALL
SELECT '28/10/2022' date, 1 is_data_present UNION ALL
SELECT '29/10/2022' date, 0 is_data_present UNION ALL
SELECT '30/10/2022' date, 0 is_data_present
)
SELECT *,
LAST_VALUE(IF(is_data_present = 1, date, NULL) IGNORE NULLS) OVER (ORDER BY date) date_to_use,
FROM sample_table;
+------------+-----------------+-------------+
| date | is_data_present | date_to_use |
+------------+-----------------+-------------+
| 27/10/2022 | 1 | 27/10/2022 |
| 28/10/2022 | 1 | 28/10/2022 |
| 29/10/2022 | 0 | 28/10/2022 |
| 30/10/2022 | 0 | 28/10/2022 |
+------------+-----------------+-------------+

How to limit a SUM using postgres

I want to limit the SUM to 2 hours in postgres sql. I dont want to limit the result of the sum, but the value that it is going to sum.
For example, the following table:
In this case, if I SUM('02:20', '01:50', '00:30', '03:00') the result would be 07:30.
|CODE | HOUR |
| --- | ---- |
| 1 | 02:20 |
| 2 | 01:50 |
| 3 | 00:30 |
| 4 | 03:00 |
But what i want, is to limit the column HOUR to 02:00. So if the value is > 02:00, it will be replaced with 02:00, only in the SUM.
So the SUM should look like this ('02:00', '01:50', '00:30', '02:00'), and the result would be 06:20
Use case as an argument of the function:
select sum(case when hour < '2:00' then hour else '2:00' end)
from my_table
Test it in Db<>fiddle.
It's still not perfect, but the idea is this:
select sum(x.anything::time)
from (select id,
time,
case when time <= '02:00' then time::text
else '02:' || (EXTRACT(MINUTES FROM time::time)::text)
end as anything
from time_table) x

I have a query that selects the sum of certain data at time intervals, how do I also get the total for these intervals?

This query produces 3 sums per time interval:
select
"interval",
SUM("mv"."bought") as "amount_bought",
SUM("mv"."sold") as "amount_sold",
SUM("mv"."transferred") as "amount_transferred"
from "mv_24hr_hourly_aggregate_buys_sells_transfers" as "mv"
inner join (
select "contract_address"
from "addresses"
where not exists (
select 1
from "address_tags"
where "address" = "addresses"."contract_address" and "tag_id" = ?
)
order by "supply_percentage" desc
) as "ca"
on "ca"."contract_address" = "mv"."contract_address"
group by "interval"
order by "interval" desc'
How can I also produce those 3 sums over every row, aka no time interval?
Example current output:
interval | amount_bought | amount_sold | amount_transferred
---------------------+---------------------+-------------------+--------------------------
2021-05-07 22:00:00 | 0 | 0 | 0
2021-05-07 21:00:00 | 0 | 0 | 0
2021-05-07 20:00:00 | 0 | 0 | 0
2021-05-07 19:00:00 | 0 | 0 | 0
2021-05-07 18:00:00 | 0 | 0 | 0
2021-05-07 17:00:00 | 0 | 0 | 0
I am looking for the total bought, total sold and total transferred over every row, in addition to the current data I collect.
Thanks!
Although grouping sets is the specific answer to the question, your query should be rewritten:
select "interval",
SUM(mv."bought") as amount_bought,
SUM(mv."sold") as amount_sold,
SUM(mv."transferred") as amount_transferred
from "mv_24hr_hourly_aggregate_buys_sells_transfers" mv join
"addresses" a
on a."contract_address" = mv."contract_address"
where not exists (
select 1
from "address_tags" at
where at."address" = a."contract_address" and at."tag_id" = ?
)
group by grouping sets ( ("interval"), () )
order by "interval" desc nulls last;
Notes:
You should really dispense with the escaped identifiers. They just make the query harder to read and write.
Don't use SQL keywords such as interval as an identifier.
Qualify all column references. You should give all tables aliases as well to simplify this.
The subquery is unnecessary.
The order by in the subquery is not only unnecessary but is ignored.
I guess, you can try to use GROUPING_SETS
https://www.postgresqltutorial.com/postgresql-grouping-sets/

How do I apply a function to each subgroup of a table in SQL

I want to find the minimum value of a column in a certain date range of a table.
so lets say I have a table like the following,
Date | Value
---------------
01-26 | 2
01-26 | 1
01-27 | 2
01-27 | 4
01-28 | 3
01-28 | 5
How can I apply the MIN() function to the subgroup of the Value column so that the result might be
Date | MIN(Value)
---------------
01-26 | 1
01-27 | 2
01-28 | 3
I thought about GROUP BY .. or such but couldn't figure out how to get the results into a table.
Using UNION and JOIN isn't quite scalable because the query could be using a date range of a month
Group by should work:
Select date, min( value )
From table1
Group by date
Maybe too simple, but seems like this would work
Select Min(col1), datecol from yourtable group by datecol;
HTH

SQL Query Compare values in per 15 minutes and display the result per hour

I have a table with 2 columns. UTCTime and Values.
The UTCTime is in 15 mins increment. I want a query that would compare the value to the previous value in one hour span and display a value between 0 and 4 depends on if the values are constant. In other words there is an entry for every 15 minute increment and the value can be constant so I just need to check each value to the previous one per hour.
For example
+---------|-------+
| UTCTime | Value |
------------------|
| 12:00 | 18.2 |
| 12:15 | 87.3 |
| 12:30 | 55.91 |
| 12:45 | 55.91 |
| 1:00 | 37.3 |
| 1:15 | 47.3 |
| 1:30 | 47.3 |
| 1:45 | 47.3 |
| 2:00 | 37.3 |
+---------|-------+
In this case, I just want a Query that would compare the 12:45 value to the 12:30 and 12:30 to 12:15 and so on. Since we are comparing in only one hour span then the constant values must be between 0 and 4 (O there is no constant values, 1 there is one like in the example above)
The query should display:
+----------+----------------+
| UTCTime | ConstantValues |
----------------------------|
| 12:00 | 1 |
| 1:00 | 2 |
+----------|----------------+
I just wanted to mention that I am new to SQL programming.
Thank you.
See SQL fiddle here
Below is the query you need and a working solution Note: I changed the timeframe to 24 hrs
;with SourceData(HourTime, Value, RowNum)
as
(
select
datepart(hh, UTCTime) HourTime,
Value,
row_number() over (partition by datepart(hh, UTCTime) order by UTCTime) RowNum
from foo
union
select
datepart(hh, UTCTime) - 1 HourTime,
Value,
5
from foo
where datepart(mi, UTCTime) = 0
)
select cast(A.HourTime as varchar) + ':00' UTCTime, sum(case when A.Value = B.Value then 1 else 0 end) ConstantValues
from SourceData A
inner join SourceData B on A.HourTime = B.HourTime and
(B.RowNum = (A.RowNum - 1))
group by cast(A.HourTime as varchar) + ':00'
select SUBSTRING_INDEX(UTCTime,':',1) as time,value, count(*)-1 as total
from foo group by value,time having total >= 1;
fiddle
Mine isn't much different from Vasanth's, same idea different approach.
The idea is that you need recursion to carry it out simply. You could also use the LEAD() function to look at rows ahead of your current row, but in this case that would require a big case statement to cover every outcome.
;WITH T
AS (
SELECT a.UTCTime,b.VALUE,ROW_NUMBER() OVER(PARTITION BY a.UTCTime ORDER BY b.UTCTime DESC)'RowRank'
FROM (SELECT *
FROM #Table1
WHERE DATEPART(MINUTE,UTCTime) = 0
)a
JOIN #Table1 b
ON b.UTCTIME BETWEEN a.UTCTIME AND DATEADD(hour,1,a.UTCTIME)
)
SELECT T.UTCTime, SUM(CASE WHEN T.Value = T2.Value THEN 1 ELSE 0 END)
FROM T
JOIN T T2
ON T.UTCTime = T2.UTCTime
AND T.RowRank = T2.RowRank -1
GROUP BY T.UTCTime
If you run the portion inside the ;WITH T AS ( ) you'll see that gets us the hour we're looking at and the values in order by time. That is used in the recursive portion below by joining to itself and evaluating each row compared to the next row (hence the RowRank - 1) on the JOIN.