How to add additional rows to result set by condition - tsql

I have a table like this:
ObjId Date Value
100 '20100401' 12
200 '20100501' 45
200 '20100401' 37
300 '20100501' 75
300 '20100401' 69
400 '20100401' 87
I have to add additional rows to result set for objId's, where there is no data at '20100501'
**100 '20100501' null**
100 '20100401' 12
200 '20100501' 45
200 '20100401' 37
300 '20100501' 75
300 '20100401' 69
**400 '20100501' null**
400 '20100401' 87
What is the best way to do this?
Here is the T-SQL script for the initial table:
declare #datesTable table (objId int, date smalldatetime, value int)
insert #datesTable
select 100, '20100401', 12
union all
select 200, '20100501', 45
union all
select 200, '20100401', 37
union all
select 300, '20100501', 75
union all
select 300, '20100401', 69
union all
select 400, '20100401', 87
select * from #datesTable

OK, I understand now :-) You want to find the objId values which have no entry with a date of 2010-05-01 and then insert extra rows with those objId and that date and a NULL value - use a CTE (Common Table Expression):
;WITH MissingObj AS
(
SELECT objId
FROM #datesTable d1
WHERE NOT EXISTS (SELECT objId FROM #datesTable d2 WHERE d2.objId = d1.objId AND d2.date = '20100501')
)
INSERT INTO #datesTable(objId, date, value)
SELECT
mo.objId, '20100501', NULL
FROM
MissingObj mo
The MissingObj CTE gets all the objId values where there's no entry for '2010-05-01', and then using that list of objId, the following INSERT statement inserts those into your #datesTable table.
As a side-note: I find this approach much easier for filling up sample tables:
declare #datesTable table (objId int, date smalldatetime, value int)
insert into #datesTable(objId, date, value)
VALUES(100, '20100401', 12),
(200, '20100501', 45),
(200, '20100401', 37),
(300, '20100501', 75) -- and so forth
SQL Server 2008 allows you to pass in multiple tuples of values in (....) - much easier and more readable than a union all construct...

This can be a solution:
declare #datesTable table (objId int, date smalldatetime, value int)
insert #datesTable
select 100, '20100401', 12
union all
select 200, '20100501', 45
union all
select 200, '20100401', 37
union all
select 300, '20100501', 75
union all
select 300, '20100401', 69
union all
select 400, '20100401', 87
with objids as (
select distinct objid
from #datesTable )
insert into #datesTable(objId, date, value)
select D.objId, '20100501', NULL
from objids D
where not exists(select *
from #datestable T
where T.objId = D.objId
and date = '20100501')
select *from #datesTable

Related

Adding a column to a table from the previous row in T-SQL

Given a row with a timestamp column and some value column (from a device) which are already in a table in Azure SQL database, I want to add a new column to the row from a most recent record which meets certain criteria (most recent will be defined by the timestamp column). The criteria is whether the value falls into a range (between 95 and 5). I want to do this for every row.
Here is an input table:
ts (Timestamp) value (integer)
------------------------------------
2019-09-22 00:00:00 90
2019-09-21 23:10:05 75
2019-09-21 23:09:00 85
2019-09-21 22:09:00 00
2019-09-21 14:09:00 70
Now I want to add a column to this table:
ts (Timestamp) value prev_value
---------------------------------------
2019-09-22 00:00:00 90 75
2019-09-21 23:10:05 75 85
2019-09-21 23:09:00 85 70
2019-09-21 22:09:00 00 70
2019-09-21 14:09:00 70 NULL
I have been trying different SQL statements but haven't bee successful so far.
So basically you want something like lag, but with a condition.
The easy way to do that is to use a correlated subquery.
First, create and populate sample table (Please save us this step in your future questions):
DECLARE #T AS TABLE
(
ts datetime2,
[value] int
)
INSERT INTO #T (ts, [value]) VALUES
('2019-09-22T00:00:00', 90),
('2019-09-21T23:10:05', 75),
('2019-09-21T23:09:00', 85),
('2019-09-21T22:09:00', 00),
('2019-09-21T14:09:00', 70);
The query:
SELECT ts,
value,
(
SELECT TOP 1 value
FROM #T T1
WHERE T0.ts > T1.ts
AND T1.value >= 5
AND T1.value <= 95
ORDER BY t1.ts DESC
) As prev_value
FROM #T T0
ORDER BY ts DESC
Results:
ts value prev_value
2019-09-22 00:00:00 90 75
2019-09-21 23:10:05 75 85
2019-09-21 23:09:00 85 70
2019-09-21 22:09:00 0 70
2019-09-21 14:09:00 70 NULL

results mismatched when retrieved dates from column of type character varying

I have two tables,i want to get the min and max date stored in table1 cfrange column which is of type character varying.
table1 and table2 is mapped using sid. i want to get the max and min date range when compared with sid of table2.
table1:
sid cfrange
100 3390
101 8000
102 5/11/2010
103 11/12/2016
104 01/03/2016
105 4000
106 4000
107 03/12/2017
108 03/11/2016
109 4/04/2018
110 10/12/2016
table2:
sid description
102 success
103 success
104 Proceeding
107 success
108 success
I tried as below but its not giving the correct min and max value.Please advice.
select max(t1.cfrange),min(t1.cfrange) from table1 t1,table2 t2 where t1.sid=t2.sid;
You should join two tables and cast cfrange as a date and cross your fingers. (May be you must format it as a date before to cast it).
create table table1 (sid int, cfrange varchar(30));
insert into table1 values
(100, '3390'),
(101, '8000'),
(102, '5/11/2010'),
(103, '11/12/2016'),
(104, '01/03/2016'),
(105, '4000'),
(106, '4000'),
(107, '03/12/2017'),
(108, '03/11/2016'),
(109, '4/04/2018'),
(110, '10/12/2016');
create table table2 (sid int, description varchar(30));
insert into table2 values
(102, 'success'),
(103, 'success'),
(104, 'Proceeding'),
(107, 'success'),
(108, 'success');
select 'Min' as caption, min(cfrange) as value
from (select table1.sid, table1.cfrange::date
from table1
inner join table2
on table1.sid = table2.sid) tt
UNION ALL
select 'Max' as caption, max(cfrange) as value
from (select table1.sid, table1.cfrange::date
from table1
inner join table2
on table1.sid = table2.sid) tt;
caption | value
:------ | :---------
Min | 2010-11-05
Max | 2017-12-03
dbfiddle here

TSQL Insert additional rows

What is the most efficient way to identify and insert rows for the following problem?
Here's my sample data
vId StartDate EndDate Distance
------------------------------------
256 2015-03-04 2015-03-05 365
271 2015-03-04 2015-03-04 86
315 2015-03-05 2015-03-06 254
256 2015-03-07 2015-03-09 150
458 2015-03-10 2015-03-12 141
458 2015-03-15 2015-03-17 85
315 2015-03-15 2015-03-16 76
I want to add additional rows for each vId where the StartDate <> EndDate like follows, so instead of just
315 2015-03-05 2015-03-06 254
256 2015-03-07 2015-03-09 150
I want to show the following
315 2015-03-05 2015-03-06 254
315 2015-03-06 2015-03-06 0
256 2015-03-07 2015-03-09 150
256 2015-03-08 2015-03-09 0
256 2015-03-09 2015-03-09 0
Thanks in advance.
Just a simple insert:
Insert Into Table(vId, StartDate, EndDate, Distance)
Select vId, DateAdd(dd, 1, StartDate), EndDate, 0
From TableName
Where StartDate <> EndDate
If you want just select but not insert then:
Select vId, StartDate, EndDate, Distance
From TableName
Union All
Select vId, DateAdd(dd, 1, StartDate), EndDate, 0
From TableName
Where StartDate <> EndDate
EDIT
This assumes that there are maximum 100 day difference. If you have longer intervals you can add more cross joins to increase possible values:
declare #t table(vId int, StartDate date, EndDate date, Distance int)
insert into #t values
(315, '2015-03-05', '2015-03-06', 254),
(256, '2015-03-07', '2015-03-09', 150)
;with cte as(select row_number() over(order by (select 1)) as rn
from (values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) t1(n)
cross join (values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) t2(n)
)
select * from #t
union all
select t1.vId, ca.StartDate, t1.EndDate, 0
from #t t1
cross apply(select dateadd(dd, c.rn, StartDate) as StartDate
from cte c
where dateadd(dd, c.rn, t1.StartDate) <= t1.EndDate) as ca
where t1.StartDate <> t1.EndDate
order by vId, StartDate
See fiddle http://sqlfiddle.com/#!6/9eecb/4641

T-SQL: finding rows in a different table without joins

I have two tables, I'll call TableA and TableB
TableA:
StartNumber EndNumber Country
1 10 USA
11 20 USA
21 30 Canada
31 40 France
41 50 France
51 60 Germany
TableB:
SomeNumber
5
15
55
22
35
46
49
For each number in TableB, I want to find the corresponding row in TableA where the number is between the StartNumber and EndNumber and return the name of the country. I then want to group these results on the country column and return the number of times each country appears. So the results would look like this:
Country Occurrences
USA 2
Germany 1
Canada 1
France 3
Not sure how to do this.
Here the query.
Select A.Country, count(*) as Occurrences
from
tableA A
inner join
tableB B
on B.someNumber between a.startnumber and b.endnumber
group by A.country
This should do the trick (but does use a join):
declare #TableA table (StartNumber int, EndNumber int, Country varchar(16));
insert into #TableA (StartNumber, EndNumber, Country)
select 1, 10, 'USA' union
select 11, 20, 'USA' union
select 21, 30, 'Canada' union
select 31, 40, 'France' union
select 41, 50, 'France' union
select 51, 60, 'Germany';
declare #TableB table (SomeNumber int);
insert into #TableB (SomeNumber)
select 5 union
select 15 union
select 55 union
select 22 union
select 35 union
select 46 union
select 49;
select
a.Country, count(*) Occurrences
from
#TableA a inner join
#TableB b on b.SomeNumber between a.StartNumber and a.EndNumber
group by
a.Country;

generate new column using other columns

I am stuck with generating a new column. The table has three columns(C_ID, C_rank, Date).
C_ID C_ Rank NewColumn(Cycle) Date
42 A 1 October 14, 2010
42 B 1 October 26, 2010
42 A 2 February 16, 2011
43 A 1 December 17, 2010
44 A 1 July 28, 2010
44 B 1 August 10, 2010
44 A 2 January 11, 2011
44 B 2 January 28, 2011
45 A 1 July 30, 2010
45 B 1 August 9, 2010
45 B 1 September 24, 2010
45 A 2 April 5, 2011
45 B 2 April 26, 2011
I want to generate one more column called Cycle in such a way that for each C_ID, it should generate the number start from one and increment the number from next C_rank = 'A' (a shown above).
I tried using row_number, but no luck.
Maybe some loop option till next C_Rank = 'A' works.
How can this be done?
You should be able to get this done using ROW_NUMBER() and PARTITION BY
;WITH YourDataCTE AS
(
SELECT
C_ID, C_Rank, Date,
ROW_NUMBER() OVER(PARTITION BY C_ID,C_Rank ORDER BY Date DESC) AS 'Cycle'
FROM
dbo.YourTable
)
SELECT *
FROM YourDataCTE
Does that do what you're looking for??
The PARTITION BY C_ID,C_Rank will cause the ROW_NUMBER to start at 1 again for each different value of C_ID,C_Rank - I didn't know what ORDER BY clause within a single partition (a single value of C_ID,C_Rank) you're looking for and just guessed it might be Date DESC (newest date first).
You could count the number of previous A's in a subquery:
select *
, (
select count(*)
from #YourTable yt2
where yt2.C_ID = yt1.C_ID
and yt2.C_Rank = 'A'
and yt2.Date <= yt1.Date
) as Cycle
from #YourTable yt1
order by
C_ID, Date
Example at ODATA.
Do a self join for all records with the same C_ID, a previous date, and a C_Rank='A' and count them.
select t1.C_ID, t1.C_Rank, count(t2.C_Rank) Cycle, t1.Date
from MyTable t1
left join MyTable t2 on t1.C_ID=t2.C_ID
and t2.Date<=t1.Date
and t2.C_Rank='A'
group by t1.C_ID, t1.C_Rank, t1.Date
order by t1.C_ID, t1.Date
Below code fulfill the requirement:
create table #Temp_Table
(
C_ID int
, C_Rank char(1)
, Date datetime
, NewColumn int
)
insert into #Temp_Table
(
C_ID
, C_Rank
, Date
)
select 42, ‘A’, ’10/14/2010′
union all
select 42, ‘B’, ’10/26/2010′
union all
select 42, ‘B’, ’10/14/2010′
union all
select 42, ‘C’, ’10/26/2010′
union all
select 42, ‘A’,’02/16/2011′
union all
select 43, ‘A’, ’12/17/2010′
union all
select 44, ‘A’, ’07/28/2010′
union all
select 44, ‘B’, ’08/10/2010′
union all
select 44, ‘A’, ’01/11/2011′
union all
select 44, ‘B’, ’01/28/2011′
union all
select 44, ‘C’, ’10/14/2010′
union all
select 44, ‘D’, ’10/26/2010′
Select ‘Original Data’ Comment
,*
from #Temp_Table
/*
This would be Actual Script to get the New ID based on information you provided
*/
Declare #Count int
,#C_ID int
,#C_Rank char(1)
,#total_Count int
,#Count_Partition int
,#Previous_ID int
Declare #Table Table (ID int IDENTITY(1,1), C_ID int, C_Rank char(1), Date datetime, NewColumn int )
Set #Count = 1
Set #Count_Partition = 0
insert into #Table
Select *
from #Temp_Table
Select #total_Count = ISNULL(MAX(ID),0)
from #Table
While #Count < = #total_Count
Begin
Select #C_ID = C_ID
,#C_Rank = C_Rank
From #Table
Where ID = #Count
If #Count = 1
Set #Previous_ID = #C_ID
If #Previous_ID != #C_ID
Set #Count_Partition = 1
Else If #C_Rank = 'A'
Set #Count_Partition = #Count_Partition + 1
update #Table
Set NewColumn = #Count_Partition
Where ID = #Count
Set #Previous_ID = #C_ID
Set #Count = #Count + 1
End
Select C_ID
, C_Rank
, [Date]
, NewColumn
from #Table
–Drop table #Temp_Table