Weird query result when the fill clause is existed - tdengine

When using the TDengine (v3.0.2), I found that the query result is some how weird when the fill clause exists in the time window query. And the following SQL statements can produce the result.
create table tu (ts timestamp, k int, z varchar(12));
insert into tu values('2022-6-1 1:1:1', 2,'b');
insert into tu values('2022-6-1 1:1:2', 3,'c');
insert into tu values('2022-6-1 1:1:3', 4,'d');
select _wstart, sum(k) from tu
where ts>='2022-06-01 1:1:0' and ts<='2022-6-1 1:1:3' interval(500a)
fill(prev) order by _wstart desc;
_wstart | sum(k) |
=================================================
2022-06-01 01:01:00.000 | NULL |
2022-06-01 01:01:00.500 | NULL |
2022-06-01 01:01:01.000 | 2 |
2022-06-01 01:01:01.500 | 2 |
2022-06-01 01:01:02.000 | 3 |
2022-06-01 01:01:02.500 | 3 |
2022-06-01 01:01:03.000 | 4 |
select _wstart, sum(k) from tu
where ts>='2022-06-01 1:1:0' and ts<='2022-6-1 1:1:3' interval(500a)
fill(prev) order by _wstart asc;
_wstart | sum(k) |
==================================================
2022-06-01 01:01:03.000 | 4 |
2022-06-01 01:01:02.500 | 3 |
2022-06-01 01:01:02.000 | 3 |
2022-06-01 01:01:01.500 | 2 |
2022-06-01 01:01:01.000 | 2 |
2022-06-01 01:01:00.500 | 2 |
2022-06-01 01:01:00.000 | 2 |
I noticed that the order by clause affected the final results, not just the order of results, but also the result in ‘2022-06-01 01:01:00.000’ and ‘2022-06-01 01:01:00.500’.
But when the fill clause does not exist, the results are identical.
I’m wondering if is this a bug? or just a standard result of the fill clause.

Related

Sql server- joining with a temp table does not return the right values

I am not sure why my query isn't returning the right values when I join a temp table with an actual table. I am using SQL Server 2008 as my database.
I have the below query
WITH TradeHistory AS (
SELECT
tran_num,
version_number,
row_creation AS last_update,
tran_status,
ROW_NUMBER() OVER(PARTITION BY tran_num ORDER BY tran_num DESC, row_creation DESC, version_number DESC) AS RowNum
FROM tran_history_view (NOLOCK)
where
row_creation BETWEEN '2018-01-22 17:02:47.083' AND '2019-01-23 19:02:47.083'
AND tran_status IN (3,5)
AND update_type NOT IN (15,52,24)
AND deal_tracking_num= 10738416
)
select ab.tran_num as ab_tran_num, ab.version_number as ab_version_num, hist.version_number as hist_version_num, ab.deal_tracking_num as ab_track_num,
ab.tran_status as ab_tran_status, hist.tran_status as hist_tran_status from
TradeHistory hist join ab_tran ab on ab.tran_num = hist.tran_num
and hist.RowNum =1
I am not sure why my join does not pick the matching row for tran_num and version_num? I get a different version_number and tran_status from tran_history table. If I join on version_number across the two tables, I don't get any rows (as expected based on the values in the final output).
My 'TradeHistory' temp table returns the below values
+----------+----------------+-------------------------+-------------+--------+
| tran_num | version_number | last_update | tran_status | RowNum |
+----------+----------------+-------------------------+-------------+--------+
| 10738416 | 2 | 2019-01-23 16:02:09.760 | 3 | 1 |
| 10738416 | 1 | 2019-01-23 16:01:51.803 | 3 | 2 |
| 10738422 | 4 | 2019-01-23 16:02:30.600 | 3 | 1 |
| 10738422 | 3 | 2019-01-23 16:02:30.243 | 3 | 2 |
| 10738422 | 2 | 2019-01-23 16:02:09.973 | 3 | 3 |
+----------+----------------+-------------------------+-------------+--------+
Result of output joining the select query with the temp table is as below. The version_number and tran_number from the two tables are different and I don't understand why? Can someone please explain?
+---------------+-------------+----------------+--------------+--------------+----------------+------------------+
| hist_tran_num | ab_tran_num | ab_version_num | hist_ver_num | ab_track_num | ab_tran_status | hist_tran_status |
+---------------+-------------+----------------+--------------+--------------+----------------+------------------+
| 10738416 | 10738416 | 4 | 2 | 10738416 | 10 | 3 |
| 10738422 | 10738422 | 6 | 4 | 10738416 | 10 | 3 |
+---------------+-------------+----------------+--------------+--------------+----------------+------------------+

SQL Server - get values from X months ago according to columndata

Let's say I have the following table (data is completely fiction):
ID | MonthDate | PersonID | Name | Status | MonthsAgoSinceLastCheck
1 | 2017-12 | 900 | Jack | Ill | -
2 | 2018-01 | 900 | Jack | Ill | 1
3 | 2018-02 | 900 | Jack | Ill | 2
4 | 2018-03 | 900 | Jack | Healthy | 1
5 | 2017-02 | 901 | Bill | Ill | -
6 | 2017-03 | 901 | Bill | Ill | 1
7 | 2017-05 | 901 | Bill | Healthy | 1
For each record, I would like to see the previous status that person had X months ago since last check (column MonthsAgoSinceLastCheck). Notice that MonthDate can skip months.
So in this case, the result would be
ID | MonthDate | PersonID | Name | Status | MonthsAgoSinceLastCheck | PreviousSatus
1 | 2017-12 | 900 | Jack | Ill | - | -
2 | 2018-01 | 900 | Jack | Ill | 1 | Ill
3 | 2018-02 | 900 | Jack | Ill | 2 | Ill
4 | 2018-03 | 900 | Jack | Healthy | 1 | Ill
5 | 2017-02 | 901 | Bill | Healthy | - | -
6 | 2017-03 | 901 | Bill | Healthy | 1 | Healthy
7 | 2017-05 | 901 | Bill | Ill | 2 | Healthy
Any sugestions/tips? I tried to do this with CTE's and self-joins but failed on both.
It's way easier to use full dates than year and months separately. The first thing you should do is generate a full date from your year + month. Then just self join with previous month, depending on the last check.
;WITH DataWithDates AS
(
SELECT
T.ID,
MonthDate = CONVERT(DATE, T.MonthDate + '-01'),
T.PersonID,
T.Name,
T.Status,
T.MonthsAgoSinceLastCheck
FROM
YourTable AS T
)
SELECT
D.ID,
D.MonthDate,
D.PersonID,
D.Name,
D.Status,
D.MonthsAgoSinceLastCheck,
PreviousStatus = N.Status
FROM
DataWithDates AS D
LEFT JOIN DataWithDates AS N ON
D.PersonID = N.PersonID AND
N.MonthDate = DATEADD(MONTH, -1 * D.MonthsAgoSinceLastCheck, D.MonthDate)
I'm assuming your MonthDate has values for all rows, otherwise the conversion will fail. I'm also assuming that your - values for MonthsAgoSinceLastCheck are actually NULL.
try this:
select *,LAG(Status) OVER(Partition by Name Order by MonthDate,Id) AS PreviousSatus
from tab1
order by id
SQl Fiddle:http://sqlfiddle.com/#!18/04407/4

Return records based on min data in source and target if conditions satisfied in sql server

Hi I have data in sql server
Table : emp
Empid | deptid | doj | loc | Status|guid
1 | 10 | 2013-09-25 | hyd | 5 |10
1 | 10 | 2014-03-25 | che | 5 |11
1 | 10 | 2014-04-09 | pune | 5 |12
1 | 10 | 2015-01-22 | pune | 5 |13
2 | 20 | 2015-12-13 | beng | 5 |14
2 | 20 | 2014-12-17 | chen | 5 |15
2 | 20 | 2010-10-15 | beng | 4 |16
Table : empref
empid | deptid | startdate | status |guid
1 | 10 | 2013-10-02 | 2 |1
1 | 10 | 2014-04-09 | 2 |2
1 | 10 | 2015-12-09 | 1 |3
1 | 10 | 2015-01-30 | 2 |4
2 | 20 | 2015-12-14 | 2 |2
2 | 20 | 2015-12-15 | 2 |3
Both tables have common columns Empid + deptid
We need to consider emp table status=5 related records compare with empref table status=2
related records and emp table doj <= startdate --empref table and days difference between less than or equal to 30 days
If we find multiple records fall within 30 days in empref table startdate then we need to consider min(startdate) corresponding records
and that records need to be considered as update. Remain status values 4 or 1 no need in the return result set at this time.
If emp table status=5 related records compare with empref table status=2
related records and emp table doj <= startdate --empref table and daysdiffernce between less than or equal 30 days
If we find multiple records fall with in 30days in emp table doj then we need to consider min(doj) corresponding records
and that record needs to be considered as update in the filter column and guid information from empref table.
Remaining records considered as insert records in the filter column and guid information from emptable.
if emp table doj <=startdate--empref table condition not satisfied or
daysdiffernce not between less than or equal 30 days then that records we need to consider insert in the filter column
based on above tables I want output like below
Empid | Deptid | loc | Status | Filter | Doj |guid
1 | 10 | hyd | 5 | Update | 2013-09-25|1
1 | 10 | che | 5 | insert | 2014-03-25|11 ------min(startdate) corresponding record
1 | 10 | pune | 5 | update | 2014-04-09|2 --------mul
1 | 10 | Pune | 5 | update | 2015-01-22|4
2 | 20 | beng | 5 | update | 2015-12-13|2 --------------min(doj) record
2 | 20 | chen | 5 | insert | 2014-12-17|15
2 | 20 | beng | 4 | insert | 2010-10-15|16 -----this record not fall the above conditions
I tried like below
select s1.*
,'Update' as Filter from emp e join empref er
on e.empid=er.empid and
e.deptid=t.deptid
and e.status='5'
and er.status='2' and
e.doj<=er.startdate and datediff(dd,er.startdate,e.doj)*-1<=30
group by er.startdate,
e.empid,e.deptid.e.doj,e.loc
having e.startdate= min(er.startdate)
In the above query not given expected result. Please help me write this query to achieve this task in sql server.
It seems like the query you supplied is very close. Here is what I quickly put together. I haven't tested it against a lot of the possible options.
select e.Empid, er.deptid, e.loc, e.[status],
case when DATEDIFF(DAY, e.doj, er.startdate) <= 0 THEN 'INSERT'
ELSE 'UPDATE' END [DaysOffset],
e.doj
FROM #emp e inner join #empref ER
on e.Empid = er.empid and
e.deptid = er.deptid
where e.[status] = 5 and er.[status] = 2
and e.doj <= er.startdate and
DATEDIFF(DAY, e.doj, er.startdate) <= 30
The CASE statement is where it determines when the record is flagged for INSERT or UPDATE. With the datediff in the WHERE clause, it will only return records that are 30 days or less.

Where to use Outer Apply

MASTER TABLE
x------x--------------------x
| Id | Name |
x------x--------------------x
| 1 | A |
| 2 | B |
| 3 | C |
x------x--------------------x
DETAILS TABLE
x------x--------------------x-------x
| Id | PERIOD | QTY |
x------x--------------------x-------x
| 1 | 2014-01-13 | 10 |
| 1 | 2014-01-11 | 15 |
| 1 | 2014-01-12 | 20 |
| 2 | 2014-01-06 | 30 |
| 2 | 2014-01-08 | 40 |
x------x--------------------x-------x
I am getting the same results when LEFT JOIN and OUTER APPLY is used.
LEFT JOIN
SELECT T1.ID,T1.NAME,T2.PERIOD,T2.QTY
FROM MASTER T1
LEFT JOIN DETAILS T2 ON T1.ID=T2.ID
OUTER APPLY
SELECT T1.ID,T1.NAME,TAB.PERIOD,TAB.QTY
FROM MASTER T1
OUTER APPLY
(
SELECT ID,PERIOD,QTY
FROM DETAILS T2
WHERE T1.ID=T2.ID
)TAB
Where should I use LEFT JOIN AND where should I use OUTER APPLY
A LEFT JOIN should be replaced with OUTER APPLY in the following situations.
1. If we want to join two tables based on TOP n results
Consider if we need to select Id and Name from Master and last two dates for each Id from Details table.
SELECT M.ID,M.NAME,D.PERIOD,D.QTY
FROM MASTER M
LEFT JOIN
(
SELECT TOP 2 ID, PERIOD,QTY
FROM DETAILS D
ORDER BY CAST(PERIOD AS DATE)DESC
)D
ON M.ID=D.ID
which forms the following result
x------x---------x--------------x-------x
| Id | Name | PERIOD | QTY |
x------x---------x--------------x-------x
| 1 | A | 2014-01-13 | 10 |
| 1 | A | 2014-01-12 | 20 |
| 2 | B | NULL | NULL |
| 3 | C | NULL | NULL |
x------x---------x--------------x-------x
This will bring wrong results ie, it will bring only latest two dates data from Details table irrespective of Id even though we join with Id. So the proper solution is using OUTER APPLY.
SELECT M.ID,M.NAME,D.PERIOD,D.QTY
FROM MASTER M
OUTER APPLY
(
SELECT TOP 2 ID, PERIOD,QTY
FROM DETAILS D
WHERE M.ID=D.ID
ORDER BY CAST(PERIOD AS DATE)DESC
)D
Here is the working : In LEFT JOIN , TOP 2 dates will be joined to the MASTER only after executing the query inside derived table D. In OUTER APPLY, it uses joining WHERE M.ID=D.ID inside the OUTER APPLY, so that each ID in Master will be joined with TOP 2 dates which will bring the following result.
x------x---------x--------------x-------x
| Id | Name | PERIOD | QTY |
x------x---------x--------------x-------x
| 1 | A | 2014-01-13 | 10 |
| 1 | A | 2014-01-12 | 20 |
| 2 | B | 2014-01-08 | 40 |
| 2 | B | 2014-01-06 | 30 |
| 3 | C | NULL | NULL |
x------x---------x--------------x-------x
2. When we need LEFT JOIN functionality using functions.
OUTER APPLY can be used as a replacement with LEFT JOIN when we need to get result from Master table and a function.
SELECT M.ID,M.NAME,C.PERIOD,C.QTY
FROM MASTER M
OUTER APPLY dbo.FnGetQty(M.ID) C
And the function goes here.
CREATE FUNCTION FnGetQty
(
#Id INT
)
RETURNS TABLE
AS
RETURN
(
SELECT ID,PERIOD,QTY
FROM DETAILS
WHERE ID=#Id
)
which generated the following result
x------x---------x--------------x-------x
| Id | Name | PERIOD | QTY |
x------x---------x--------------x-------x
| 1 | A | 2014-01-13 | 10 |
| 1 | A | 2014-01-11 | 15 |
| 1 | A | 2014-01-12 | 20 |
| 2 | B | 2014-01-06 | 30 |
| 2 | B | 2014-01-08 | 40 |
| 3 | C | NULL | NULL |
x------x---------x--------------x-------x
3. Retain NULL values when unpivoting
Consider you have the below table
x------x-------------x--------------x
| Id | FROMDATE | TODATE |
x------x-------------x--------------x
| 1 | 2014-01-11 | 2014-01-13 |
| 1 | 2014-02-23 | 2014-02-27 |
| 2 | 2014-05-06 | 2014-05-30 |
| 3 | NULL | NULL |
x------x-------------x--------------x
When you use UNPIVOT to bring FROMDATE AND TODATE to one column, it will eliminate NULL values by default.
SELECT ID,DATES
FROM MYTABLE
UNPIVOT (DATES FOR COLS IN (FROMDATE,TODATE)) P
which generates the below result. Note that we have missed the record of Id number 3
x------x-------------x
| Id | DATES |
x------x-------------x
| 1 | 2014-01-11 |
| 1 | 2014-01-13 |
| 1 | 2014-02-23 |
| 1 | 2014-02-27 |
| 2 | 2014-05-06 |
| 2 | 2014-05-30 |
x------x-------------x
In such cases an APPLY can be used(either CROSS APPLY or OUTER APPLY, which is interchangeable).
SELECT DISTINCT ID,DATES
FROM MYTABLE
OUTER APPLY(VALUES (FROMDATE),(TODATE))
COLUMNNAMES(DATES)
which forms the following result and retains Id where its value is 3
x------x-------------x
| Id | DATES |
x------x-------------x
| 1 | 2014-01-11 |
| 1 | 2014-01-13 |
| 1 | 2014-02-23 |
| 1 | 2014-02-27 |
| 2 | 2014-05-06 |
| 2 | 2014-05-30 |
| 3 | NULL |
x------x-------------x
In your example queries the results are indeed the same.
But OUTER APPLY can do more: For each outer row you can produce an arbitrary inner result set. For example you can join the TOP 1 ORDER BY ... row. A LEFT JOIN can't do that.
The computation of the inner result set can reference outer columns (like your example did).
OUTER APPLY is strictly more powerful than LEFT JOIN. This is easy to see because each LEFT JOIN can be rewritten to an OUTER APPLY just like you did. It's syntax is more verbose, though.

Finding the max and min date values in SQL Server tables

I have two tables:
A lookup table (tabOne):
KEY | Group | Name | Desc | Val_Key
----------------------------------------
1 | a | NameA | DescA | 10
2 | b | NameB | DescB | 20
3 | c | NameC | DescC | 30
4 | d | NameD | DescD | 40
5 | e | NameE | DescE | 50
6 | f | NameF | DescF | 60
A second table containing readings (tabTwo):
KEY | Date | Reading | Val_Key
----------------------------------------
1 | Date | Read | 10
2 | Date | Read | 20
3 | Date | Read | 40
4 | Date | Read | 40
5 | Date | Read | 30
6 | Date | Read | 20
7 | Date | Read | 40
8 | Date | Read | 20
9 | Date | Read | 10
10 | Date | Read | 20
11 | Date | Read | 50
12 | Date | Read | 60
What I need to do is join tabTwo with TabOne and create a column with the newest Reading and a column with the oldest reading for each item in the group column of TabOne.
At the end of the day I want a table that look as follow:
KEY | Group | Name | Desc | Val_Key | LastReading | FirstReading |
-------------------------------------------------------------------------
1 | a | NameA | DescA | 10 | | |
2 | b | NameB | DescB | 20 | | |
3 | c | NameC | DescC | 30 | | |
4 | d | NameD | DescD | 40 | | |
5 | e | NameE | DescE | 50 | | |
6 | f | NameF | DescF | 60 | | |
Thanks!
Freddie
If this is Sql Server 2005 or newer, outer apply will help:
select TabOne.*,
last.Reading LastReading,
first.Reading FirstReading
from TabOne
outer apply
(
select top 1
Reading
from TabTwo
where TabTwo.Val_Key = TabOne.val_Key
order by TabTwo.Date desc
) last
outer apply
(
select top 1
Reading
from TabTwo
where TabTwo.Val_Key = TabOne.val_Key
order by TabTwo.Date asc
) first
Live test is # Sql Fiddle.
#Nikola Markovinović's solution can be made more universally applicable if the subqueries are moved directly to the main query's SELECT clause, which is possible each of them retrieves only one value and is, therefore, valid as a scalar expression:
SELECT
t1.[KEY],
t1.[Group],
t1.Name,
t1.[Desc],
t1.Val_Key,
(
SELECT TOP 1 Reading
FROM TabTwo
WHERE Val_Key = t1.Val_Key
ORDER BY Date DESC
) AS LastReading,
(
SELECT TOP 1 Reading
FROM TabTwo
WHERE Val_Key = t1.Val_Key
ORDER BY Date ASC
) AS FirstReading
FROM TabOne t1
If you needed e.g. dates along the way, you would probably have to stick to Nikola's solution. There is an alternative to it, but it's more cumbersome (albeit more standard too): it would involve grouping TabTwo's data by Val_Key to get earliest/latest dates per Val_Key, then joining back to TabTwo to access entire rows corresponding to the found dates to finally pull the necessary columns, and ultimately joining both result sets to TabOne to get the final column set.

Resources