Comparing two numbers strings and getting out the unique values - sql

Here is a sample table I made to better illustrate my problem:
Create Table SampleTable(
TableID int,
NumberRow nvarchar(500)
)
Insert into SampleTable Values(1, '15,21,23,41,44,5,50,59,6,')
Insert into SampleTable Values(2, '10,24,29,41,5,50,59,6,73,')
Insert into SampleTable Values(3, '10,15,21,24,29,33,41,50,59,60,61,62,66,73,')
Insert into SampleTable Values(4, '10,15,21,24,28,33,37,41,44,5,50,6,60,61,62,66,')
Insert into SampleTable Values(5, '15,24,33,41,5,6,61,62,66,73,')
TableID NumberRow
---------------------------------
1 15,21,23,41,44,5,50,59,6,
2 10,24,29,41,5,50,59,6,73,
3 10,15,21,24,29,33,41,50,59,60,61,62,66,73,
4 10,15,21,24,28,33,37,41,44,5,50,6,60,61,62,66,
5 15,24,33,41,5,6,61,62,66,73,
After that I wrote a self join query:
Select
t1.TableID AS ID1,
t2.TableID AS ID2,
t1.NumberRow AS Numbers1,
t2.NumberRow AS Numbers2
From SampleTable t1
inner join SampleTable t2
on t1.TableID = t2.TableID - 1
Order by t2.TableID asc
Which results in:
ID1 ID2 Numbers1 Numbers2
-------------------------------------------------------------------------------------
1 2 15,21,23,41,44,5,50,59,6, 10,24,29,41,5,50,59,6,73,
2 3 10,24,29,41,5,50,59,6,73, 10,15,21,24,29,33,41,50,59,60,61,62,66,73,
3 4 10,15,21,24,29,33,41,50,59,60,61,62,66,73, 10,15,21,24,28,33,37,41,44,5,50,6,60,61,62,66,
4 5 10,15,21,24,28,33,37,41,44,5,50,6,60,61,62,66, 15,24,33,41,5,6,61,62,66,73,
Now I want to make two columns that show a string of numbers that are unique to both of the columns (Numbers1 and Numbers2).
So far I haven't come up with any solutions. My other approach was to make numbers be in a column instead of a string, but I still couldn't figure out how I could resolve my problem.

Maybe this query can be helpful but I agree with the design considerations about your table
select DISTINCT value as NUMBER1 ,'' AS NUMBER2 from (
Select
t1.TableID AS ID1,
t2.TableID AS ID2,
t1.NumberRow AS Numbers1,
t2.NumberRow AS Numbers2
From SampleTable t1
inner join SampleTable t2
on t1.TableID = t2.TableID - 1
) as tmp_tbl
CROSS APPLY string_split(tmp_tbl.Numbers1,',')
UNION ALL
select DISTINCT '', value from (
Select
t1.TableID AS ID1,
t2.TableID AS ID2,
t1.NumberRow AS Numbers1,
t2.NumberRow AS Numbers2
From SampleTable t1
inner join SampleTable t2
on t1.TableID = t2.TableID - 1
) as tmp_tbl
CROSS APPLY string_split(tmp_tbl.Numbers1,',')
+---------+---------+
| NUMBER1 | NUMBER2 |
+---------+---------+
| | |
| 10 | |
| 15 | |
| 21 | |
| 23 | |
| 24 | |
| 28 | |
| 29 | |
| 33 | |
| 37 | |
| 41 | |
| 44 | |
| 5 | |
| 50 | |
| 59 | |
| 6 | |
| 60 | |
| 61 | |
| 62 | |
| 66 | |
| 73 | |
| | |
| | 10 |
| | 15 |
| | 21 |
| | 23 |
| | 24 |
| | 28 |
| | 29 |
| | 33 |
| | 37 |
| | 41 |
| | 44 |
| | 5 |
| | 50 |
| | 59 |
| | 6 |
| | 60 |
| | 61 |
| | 62 |
| | 66 |
| | 73 |
+---------+---------+

Related

SQL - Adding a total line for each distinct value in column 1

I am trying to add a "total count" line for each distinct value coming from my first column. I tired to make a UNION ALL but made a total mess of things. (based on the TL.OP_CODE value.)
Any help GREATLY appreciated. Sorry for my formatting...
select
tl.op_code as "OPS Code", tl.current_status AS "Status", tl.service_level as "Service",
tl.Bill_number as "Bill number", tl.trace_no as "Trace No",
tl.pick_up_by AS "PU By", tl.origname, tl.origcity, tl.origprov,
tl.deliver_by AS "DEL By", tl.destname, tl.destcity, tl.destprov, tl.pallets
from tlorder tl
where tl.current_Status NOT IN ('CANCL')
order by tl.op_code, tl.currency_code, tl.pick_up_by
Try the following approach:
WITH MYTAB (op_code, A, B) AS
(
-- You may put your original SELECT here
-- instead of this artificial VALUES
VALUES
(1, 1, 1)
, (1, 1, 2)
, (2, 1, 1)
)
SELECT op_code, A, B, CAST (NULL AS INT) AS CNT
FROM MYTAB T
UNION ALL
SELECT op_code, NULL, NULL, COUNT (1) CNT
FROM MYTAB
GROUP BY op_code
ORDER BY op_code, CNT DESC;
OP_CODE
A
B
CNT
1
1
1
1
1
2
1
2
2
1
1
2
1
Use Rollup for example if I have student_score table and I am trying to get sum of scores for distinct scores here.
| student_id | score |
| ---------- | ----- |
| 1 | 95 |
| 2 | 95 |
| 3 | 95 |
| 4 | 93 |
| 5 | 93 |
| 6 | 98 |
| 7 | 98 |
| 8 | 99 |
| 9 | 91 |
| 10 | 91 |
SELECT student_id, sum(score) as score from student_score GROUP BY ROLLUP(score, student_id) ORDER BY score;
| student_id | score |
| ---------- | ----- |
| NULL | 948 |
| 9 | 91 |
| 10 | 91 |
| NULL | 182 |
| 4 | 93 |
| 5 | 93 |
| NULL | 186 |
| 1 | 95 |
| 2 | 95 |
| 3 | 95 |
| NULL | 285 |
| 6 | 98 |
| 7 | 98 |
| NULL | 196 |
| 8 | 99 |
| NULL | 99 |

Replace nulls of a column with column value from another table

I have data flowing from two tables, table A and table B. I'm doing an inner join on a common column from both the tables and creating two more new columns based on different conditions. Below is a sample dataset:
Table A
| Id | StartDate |
|-----|------------|
| 119 | 01-01-2018 |
| 120 | 01-02-2019 |
| 121 | 03-05-2018 |
| 123 | 05-08-2021 |
TABLE B
| Id | CodeId | Code | RedemptionDate |
|-----|--------|------|----------------|
| 119 | 1 | abc | null |
| 119 | 2 | abc | null |
| 119 | 3 | def | null |
| 119 | 4 | def | 2/3/2019 |
| 120 | 5 | ghi | 04/7/2018 |
| 120 | 6 | ghi | 4/5/2018 |
| 121 | 7 | jkl | null |
| 121 | 8 | jkl | 4/4/2019 |
| 121 | 9 | mno | 3/18/2020 |
| 123 | 10 | pqr | null |
What I'm basically doing is joining the tables on column 'Id' when StartDate>2018 and create two new columns - 'unlock' by counting CodeId when RedemptionDate is null and 'Redeem' by counting CodeId when RedmeptionDate is not null. Below is the SQL query:
WITH cte1 AS (
SELECT a.id, COUNT(b.CodeId) AS 'Unlock'
FROM TableA AS a
JOIN TableB AS b ON a.Id=b.Id
WHERE YEAR(a.StartDate) >= 2018 AND b.RedemptionDate IS NULL
GROUP BY a.id
), cte2 AS (
SELECT a.id, COUNT(b.CodeId) AS 'Redeem'
FROM TableA AS a
JOIN TableB AS b ON a.Id=b.Id
WHERE YEAR(a.StartDate) >= 2018 AND b.RedemptionDate IS NOT NULL
GROUP BY a.id
)
SELECT cte1.Id, cte1.Unlocked, cte2.Redeemed
FROM cte1
FULL OUTER JOIN cte2 ON cte1.Id = cte2.Id
If I break down the output of this query, result from cte1 will look like below:
| Id | Unlock |
|-----|--------|
| 119 | 3 |
| 121 | 1 |
| 123 | 1 |
And from cte2 will look like below:
| Id | Redeem |
|-----|--------|
| 119 | 1 |
| 120 | 2 |
| 121 | 2 |
The last select query will produce the following result:
| Id | Unlock | Redeem |
|------|--------|--------|
| 119 | 3 | 1 |
| null | null | 2 |
| 121 | 1 | 2 |
| 123 | 1 | null |
How can I replace the null value from Id with values from 'b.Id'? If I try coalesce or a case statement, they create new columns. I don't want to create additional columns, rather replace the null values from the column values coming from another table.
My final output should like:
| Id | Unlock | Redeem |
|-----|--------|--------|
| 119 | 3 | 1 |
| 120 | null | 2 |
| 121 | 1 | 2 |
| 123 | 1 | null |
If I'm following correctly, you can use apply with aggregation:
select a.*, b.*
from a cross apply
(select count(RedemptionDate) as num_redeemed,
count(*) - count(RedemptionDate) as num_unlock
from b
where b.id = a.id
) b;
However, the answer to your question is to use coalesce(cte1.id, cte2.id) as id.

How to get values of rows and columns

I have two tables.
Student Table
Property Table
Result Table
How can I get the value of Student Table and the property ID of the column fron the Property table and merge that into the Result table?
Any advice would be helpful.
Update #1:
I tried using Christian Moen 's suggestion, this is what i get.
You need to UNPIVOT the Student's columns first, to get the columns (properties names) in one column as rows. Then join with the Property table based on the property name like this:
WITH UnPivoted
AS
(
SELECT ID, value,col
FROM
(
SELECT ID,
CAST(Name AS NVARCHAR(50)) AS Name,
CAST(Class AS NVARCHAR(50)) AS Class,
CAST(ENG AS NVARCHAR(50)) AS ENG,
CAST(TAM AS NVARCHAR(50)) AS TAM,
CAST(HIN AS NVARCHAR(50)) AS HIN,
CAST(MAT AS NVARCHAR(50)) AS MAT,
CAST(PHY AS NVARCHAR(50)) AS PHY
FROM Student
) AS s
UNPIVOT
(value FOR col IN
([Name], [class], [ENG], [TAM], [HIN], [MAT], [PHY])
)AS unpvt
)
SELECT
ROW_NUMBER() OVER(ORDER BY u.ID,PropertyID) AS ID,
p.PropertyID,
u.Value,
u.ID AS StudID
FROM Property AS p
INNER JOIN UnPivoted AS u ON p.PropertyName = u.col;
For the first ID, I used the ranking function ROW_NUMBER() to generate this sequence id.
This will give the exact results that you are looking for.
Results:
| ID | PropertyID | Value | StudID |
|----|------------|--------|--------|
| 1 | 1 | Jack | 1 |
| 2 | 2 | 10 | 1 |
| 3 | 3 | 89 | 1 |
| 4 | 4 | 88 | 1 |
| 5 | 5 | 45 | 1 |
| 6 | 6 | 100 | 1 |
| 7 | 7 | 98 | 1 |
| 8 | 1 | Jill | 2 |
| 9 | 2 | 10 | 2 |
| 10 | 3 | 89 | 2 |
| 11 | 4 | 99 | 2 |
| 12 | 5 | 100 | 2 |
| 13 | 6 | 78 | 2 |
| 14 | 7 | 91 | 2 |
| 15 | 1 | Trevor | 3 |
| 16 | 2 | 12 | 3 |
| 17 | 3 | 100 | 3 |
| 18 | 4 | 50 | 3 |
| 19 | 5 | 49 | 3 |
| 20 | 6 | 94 | 3 |
| 21 | 7 | 100 | 3 |
| 22 | 1 | Jim | 4 |
| 23 | 2 | 8 | 4 |
| 24 | 3 | 100 | 4 |
| 25 | 4 | 91 | 4 |
| 26 | 5 | 92 | 4 |
| 27 | 6 | 100 | 4 |
| 28 | 7 | 100 | 4 |
Other option is to use of apply if you don't want to go unpivot way
select row_number() over (order by (select 1)) ID, p.PropertyID [PropID], a.Value, a.StuID
from Student s
cross apply
(
values (s.ID, 'Name', s.Name),
(s.ID, 'Class', cast(s.Class as varchar)),
(s.ID, 'ENG', cast(s.ENG as varchar)),
(s.ID, 'TAM', cast(s.TAM as varchar)),
(s.ID, 'HIN', cast(s.HIN as varchar)),
(s.ID, 'MAT', cast(s.MAT as varchar)),
(s.ID, 'PHY', cast(s.PHY as varchar))
) as a(StuID, Property, Value)
join Property p on p.PropertyName = a.Property

Transposing Data SQL

The data looks similar to this:
+----+------+-----------+-------+---------+---------+--------+
| ID | Unit | Floorplan | Sq Ft | Name | Amenity | Charge |
+----+------+-----------+-------+---------+---------+--------+
| 1 | 110 | A1 | 750 | Alan | GARAGE | 50 |
| 2 | | | | | RENT | 850 |
| 3 | | | | | PEST | 2 |
| 4 | | | | | TRASH | 15 |
| 5 | | | | | TOTAL | 20 |
| 6 | 111 | A2 | 760 | Bill | STORAGE | 35 |
| 7 | | | | | GARAGE | 50 |
| 8 | | | | | RENT | 850 |
| 9 | | | | | PEST | 2 |
| 10 | | | | | TOTAL | 15 |
| 11 | 112 | A3 | 770 | Charlie | PETRENT | 20 |
| 12 | | | | | STORAGE | 35 |
| 13 | | | | | GARAGE | 50 |
| 14 | | | | | RENT | 850 |
| 15 | | | | | TOTAL | 2 |
+----+------+-----------+-------+---------+---------+--------+
I am new to SQL and trying my best using Microsoft Access, but I need help.
The data needs to look like this:
My first step is to separate the units from the rest with
SELECT * FROM table WHERE Unit <> NULL;
and after that I've usually just hard-input the rest.
My idea was as follows:
INSERT INTO table
VALUES (NULL,NULL,...,'Pest',$2)
FROM table
WHERE NOT EXIST 'Pest' BETWEEN x AND y
/* where x = Total 1 and y = Total 2*/
Am I on the right track? I probably need a loop or a join, but I'm not at that level yet.
You can use a crosstab query, though a bit convoluted it is:
TRANSFORM
Sum(TableUnit.Charge) AS SumOfCharge
SELECT
S.Unit,
S.Floorplan,
S.SqFt,
S.Name,
S.Amenity
FROM
TableUnit,
(SELECT
Q.Id,
Val(DMax("Id","TableUnit","Id<=" & Q.[Id] & " And Unit Is Not Null")) AS ParentId
FROM TableUnit As Q) AS T,
(SELECT
TableUnit.Id,
TableUnit.Unit,
TableUnit.Floorplan,
TableUnit.SqFt,
TableUnit.Name,
TableUnit.Amenity
FROM
TableUnit
WHERE
TableUnit.Unit Is Not Null) AS S
WHERE
TableUnit.Id=[T].[Id]
AND
T.ParentId)=[S].[Id]
GROUP BY
T.ParentId,
S.Unit,
S.Floorplan,
S.SqFt,
S.Name,
S.Amenity
PIVOT
TableUnit.Amenity In
("Garage","Pest","Trash","PetRent","Storage","Rent");
Your test data differs a little from your expected output, so:
My MSAccess is rather rusty, but something like this should work:
SELECT t0.Unit, t0.Floorplan, t0.[Sq Ft], t0.Name, t0.Amenity
, SUM(IIF(tM.Amenity = 'GARAGE', Charge, 0)) AS [Garage]
, SUM(IIF(tM.Amenity = 'PEST', Charge, 0)) AS [Pest]
FROM (
SELECT t1.id AS id0, MIN(t2.id) AS idN
FROM t AS t1
INNER JOIN t AS t2 ON t1.id < t2.id
WHERE t1.Unit <> '' AND t2.Unit <> ''
) AS groups
INNER JOIN t AS t0 ON t0.id = groups.id0
LEFT JOIN t AS tM ON tM.id > groups.id0 AND tm.id < groups.idN
GROUP BY t0.Unit, t0.Floorplan, t0.[Sq Ft], t0.Name, t0.Amenity
;
Though, if I remember correctly, and it hasn't changed in newer versions; you can't have true subqueries and will need to make groups a separate query you can join to as if it were a table/view.

Self-Joins, Cross-Joins and Grouping

I've got a table of temperature samples over time from several sources and I want to find the minimum, maximum, and average temperatures across all sources at set time intervals. At first glance this is easily done like so:
SELECT MIN(temp), MAX(temp), AVG(temp) FROM samples GROUP BY time;
However, things become much more complicated (to the point of where I'm stumped!) if sources drop in and out and rather than ignoring the missing sources during the intervals in question I want to use the sources' last know temperatures for the missing samples. Using datetimes and constructing intervals (say every minute) across samples unevenly distributed over time further complicates things.
I think it should be possible to create the results I want by doing a self-join on the samples table where the time from the first table is greater than or equal to the time of the second table and then calculating aggregate values for rows grouped by source. However, I'm stumped about how to actually do this.
Here's my test table:
+------+------+------+
| time | source | temp |
+------+------+------+
| 1 | a | 20 |
| 1 | b | 18 |
| 1 | c | 23 |
| 2 | b | 21 |
| 2 | c | 20 |
| 2 | a | 18 |
| 3 | a | 16 |
| 3 | c | 13 |
| 4 | c | 15 |
| 4 | a | 4 |
| 4 | b | 31 |
| 5 | b | 10 |
| 5 | c | 16 |
| 5 | a | 22 |
| 6 | a | 18 |
| 6 | b | 17 |
| 7 | a | 20 |
| 7 | b | 19 |
+------+------+------+
INSERT INTO samples (time, source, temp) VALUES (1, 'a', 20), (1, 'b', 18), (1, 'c', 23), (2, 'b', 21), (2, 'c', 20), (2, 'a', 18), (3, 'a', 16), (3, 'c', 13), (4, 'c', 15), (4, 'a', 4), (4, 'b', 31), (5, 'b', 10), (5, 'c', 16), (5, 'a', 22), (6, 'a', 18), (6, 'b', 17), (7, 'a', 20), (7, 'b', 19);
To do my min, max and avg calculations, I want an intermediate table that looks like this:
+------+------+------+
| time | source | temp |
+------+------+------+
| 1 | a | 20 |
| 1 | b | 18 |
| 1 | c | 23 |
| 2 | b | 21 |
| 2 | c | 20 |
| 2 | a | 18 |
| 3 | a | 16 |
| 3 | b | 21 |
| 3 | c | 13 |
| 4 | c | 15 |
| 4 | a | 4 |
| 4 | b | 31 |
| 5 | b | 10 |
| 5 | c | 16 |
| 5 | a | 22 |
| 6 | a | 18 |
| 6 | b | 17 |
| 6 | c | 16 |
| 7 | a | 20 |
| 7 | b | 19 |
| 7 | c | 16 |
+------+------+------+
The following query is getting me close to what I want but it takes the temperature value of the source's first result, rather than the most recent one at the given time interval:
SELECT s.dt as sdt, s.mac, ss.temp, MAX(ss.dt) as maxdt FROM (SELECT DISTINCT dt FROM samples) AS s CROSS JOIN samples AS ss WHERE s.dt >= ss.dt GROUP BY sdt, mac HAVING maxdt <= s.dt ORDER BY sdt ASC, maxdt ASC;
+------+------+------+-------+
| sdt | mac | temp | maxdt |
+------+------+------+-------+
| 1 | a | 20 | 1 |
| 1 | c | 23 | 1 |
| 1 | b | 18 | 1 |
| 2 | a | 20 | 2 |
| 2 | c | 23 | 2 |
| 2 | b | 18 | 2 |
| 3 | b | 18 | 2 |
| 3 | a | 20 | 3 |
| 3 | c | 23 | 3 |
| 4 | a | 20 | 4 |
| 4 | c | 23 | 4 |
| 4 | b | 18 | 4 |
| 5 | a | 20 | 5 |
| 5 | c | 23 | 5 |
| 5 | b | 18 | 5 |
| 6 | c | 23 | 5 |
| 6 | a | 20 | 6 |
| 6 | b | 18 | 6 |
| 7 | c | 23 | 5 |
| 7 | b | 18 | 7 |
| 7 | a | 20 | 7 |
+------+------+------+-------+
Update: chadhoc (great name, by the way!) gives a nice solution that unfortunately does not work in MySQL, since it does not support the FULL JOIN he uses. Luckily, I believe a simple UNION is an effective replacement:
-- Unify the original samples with the missing values that we've calculated
(
SELECT time, source, temp
FROM samples
)
UNION
( -- Pull all the time/source combinations that we are missing from the sample set, along with the temp
-- from the last sampled interval for the same time/source combination if we do not have one
SELECT a.time, a.source, (SELECT t2.temp FROM samples AS t2 WHERE t2.time < a.time AND t2.source = a.source ORDER BY t2.time DESC LIMIT 1) AS temp
FROM
( -- All values we want to get should be a cross of time/temp
SELECT t1.time, s1.source
FROM
(SELECT DISTINCT time FROM samples) AS t1
CROSS JOIN
(SELECT DISTINCT source FROM samples) AS s1
) AS a
LEFT JOIN samples s
ON a.time = s.time
AND a.source = s.source
WHERE s.source IS NULL
)
ORDER BY time, source;
Update 2: MySQL gives the following EXPLAIN output for chadhoc's code:
+----+--------------------+------------+------+---------------+------+---------+------+------+-----------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+------------+------+---------------+------+---------+------+------+-----------------------------+
| 1 | PRIMARY | temp | ALL | NULL | NULL | NULL | NULL | 18 | |
| 2 | UNION | <derived4> | ALL | NULL | NULL | NULL | NULL | 21 | |
| 2 | UNION | s | ALL | NULL | NULL | NULL | NULL | 18 | Using where |
| 4 | DERIVED | <derived6> | ALL | NULL | NULL | NULL | NULL | 3 | |
| 4 | DERIVED | <derived5> | ALL | NULL | NULL | NULL | NULL | 7 | |
| 6 | DERIVED | temp | ALL | NULL | NULL | NULL | NULL | 18 | Using temporary |
| 5 | DERIVED | temp | ALL | NULL | NULL | NULL | NULL | 18 | Using temporary |
| 3 | DEPENDENT SUBQUERY | t2 | ALL | NULL | NULL | NULL | NULL | 18 | Using where; Using filesort |
| NULL | UNION RESULT | <union1,2> | ALL | NULL | NULL | NULL | NULL | NULL | Using filesort |
+----+--------------------+------------+------+---------------+------+---------+------+------+-----------------------------+
I was able to get Charles' code working like so:
SELECT T.time, S.source,
COALESCE(
D.temp,
(
SELECT temp FROM samples
WHERE source = S.source AND time = (
SELECT MAX(time)
FROM samples
WHERE
source = S.source
AND time < T.time
)
)
) AS temp
FROM (SELECT DISTINCT time FROM samples) AS T
CROSS JOIN (SELECT DISTINCT source FROM samples) AS S
LEFT JOIN samples AS D
ON D.source = S.source AND D.time = T.time
Its explanation is:
+----+--------------------+------------+------+---------------+------+---------+------+------+-----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+------------+------+---------------+------+---------+------+------+-----------------+
| 1 | PRIMARY | <derived5> | ALL | NULL | NULL | NULL | NULL | 3 | |
| 1 | PRIMARY | <derived4> | ALL | NULL | NULL | NULL | NULL | 7 | |
| 1 | PRIMARY | D | ALL | NULL | NULL | NULL | NULL | 18 | |
| 5 | DERIVED | temp | ALL | NULL | NULL | NULL | NULL | 18 | Using temporary |
| 4 | DERIVED | temp | ALL | NULL | NULL | NULL | NULL | 18 | Using temporary |
| 2 | DEPENDENT SUBQUERY | temp | ALL | NULL | NULL | NULL | NULL | 18 | Using where |
| 3 | DEPENDENT SUBQUERY | temp | ALL | NULL | NULL | NULL | NULL | 18 | Using where |
+----+--------------------+------------+------+---------------+------+---------+------+------+-----------------+
I think you'll get better performance making use of the ranking/windowing functions in mySql, but unfortunately I do not know those as well as the TSQL implementation. Here is an ANSI compliant solution that will work though:
-- Full join across the sample set and anything missing from the sample set, pulling the missing temp first if we do not have one
select coalesce(c1.[time], c2.[time]) as dt, coalesce(c1.source, c2.source) as source, coalesce(c2.temp, c1.temp) as temp
from samples c1
full join ( -- Pull all the time/source combinations that we are missing from the sample set, along with the temp
-- from the last sampled interval for the same time/source combination if we do not have one
select a.time, a.source,
(select top 1 t2.temp from samples t2 where t2.time < a.time and t2.source = a.source order by t2.time desc) as temp
from
( -- All values we want to get should be a cross of time/samples
select t1.[time], s1.source
from
(select distinct [time] from samples) as t1
cross join
(select distinct source from samples) as s1
) a
left join samples s
on a.[time] = s.time
and a.source = s.source
where s.source is null
) c2
on c1.time = c2.time
and c1.source = c2.source
order by dt, source
I know this looks complicated, but it's formatted to explain itself...
It should work... Hope you only have three sources... If you have an arbitrary number of sources than this won't work... In that case see the second query...
EDIT: Removed first attempt
EDIT: If you don't know the sources ahead of time, you'll have to do something where you create an intermediate result set that "Fills in" the missing values..
something like this:
2nd EDIT: Removed need for Coalesce by moving logic to retrieve most recent temp reading for each source from Select clause into the Join condition.
Select T.Time, Max(Temp) MaxTemp,
Min(Temp) MinTemp, Avg(Temp) AvgTemp
From
(Select T.TIme, S.Source, D.Temp
From (Select Distinct Time From Samples) T
Cross Join
(Select Distinct Source From Samples) S
Left Join Samples D
On D.Source = S.Source
And D.Time =
(Select Max(Time)
From Samples
Where Source = S.Source
And Time <= T.Time)) Z
Group By T.Time