Joining records from same table - sql

There is a table test that contains data as shown below:
id id1 id2 id3 date1
1 1 2 2500 2010-09-30 00:00:00.000
2 1 2 4700 2005-01-01 00:00:00.000
3 1 2 4700 2009-08-01 00:00:00.000
4 1 3 2500 2010-09-30 00:00:00.000
5 1 3 4700 2003-02-01 00:00:00.000
6 1 8 4000 2007-04-01 00:00:00.000
7 1 8 4000 2013-09-01 00:00:00.000
8 1 8 4060 2007-04-01 00:00:00.000
9 1 8 8500 2010-09-30 00:00:00.000
What I need to do is order this data in the following format:
id1 id2 id3 date1 date2
1 2 2500 2010-09-30 00:00:00.000 9999-12-31 23:59:59.997
1 2 4700 2005-01-01 00:00:00.000 2009-07-31 00:00:00.000
1 2 4700 2009-08-01 00:00:00.000 9999-12-31 23:59:59.997
1 3 2500 2010-09-30 00:00:00.000 9999-12-31 23:59:59.997
1 3 4700 2003-02-01 00:00:00.000 9999-12-31 23:59:59.997
1 8 4000 2007-04-01 00:00:00.000 2013-08-31 00:00:00.000
1 8 4000 2013-09-01 00:00:00.000 9999-12-31 23:59:59.997
1 8 4060 2007-04-01 00:00:00.000 9999-12-31 23:59:59.997
1 8 8500 2010-09-30 00:00:00.000 9999-12-31 23:59:59.997
Using the following logic:
If there is only one record for the same id1, id2 and id3 we use the original date for date1 and we use the maximum date available in sql server (9999-12-31) for date2.
If there is more than one record for the same id1, id2 and id3 we still keep the original date for date1 and we use for date2 the original date field minus 1 day from the next record . The last record will use also the maximum date available in sql server (9999-12-31) for date2.
I manage to build the following query but it is not perfect as it brings some invalid records:
select * from
(select
t1.id1,t1.id2,t1.id3,t1.date1,
case
when t1.date1=t2.date1 then CONVERT(DATETIME, '12/31/9999 23:59:59.997')
else DATEADD(day, -1, t2.date1)
end as date2
from test t1
inner join test t2
on t1.id1=t2.id1 and t1.id2=t2.id2 and t1.id3=t2.id3
) sub
where date2>=date1
order by id1,id2,id3,date1 asc
The result of the query is:
id1 id2 id3 date1 date2
1 2 2500 2010-09-30 00:00:00.000 9999-12-31 23:59:59.997
1 2 4700 2005-01-01 00:00:00.000 9999-12-31 23:59:59.997 *
1 2 4700 2005-01-01 00:00:00.000 2009-07-31 00:00:00.000
1 2 4700 2009-08-01 00:00:00.000 9999-12-31 23:59:59.997
1 3 2500 2010-09-30 00:00:00.000 9999-12-31 23:59:59.997
1 3 4700 2003-02-01 00:00:00.000 9999-12-31 23:59:59.997
1 8 4000 2007-04-01 00:00:00.000 9999-12-31 23:59:59.997 *
1 8 4000 2007-04-01 00:00:00.000 2013-08-31 00:00:00.000
1 8 4000 2013-09-01 00:00:00.000 9999-12-31 23:59:59.997
1 8 4060 2007-04-01 00:00:00.000 9999-12-31 23:59:59.997
1 8 8500 2010-09-30 00:00:00.000 9999-12-31 23:59:59.997
As you can see, the records marked with * are not necessary but I don't know how to get rid off that rows.

This should do:
select id1,id2,id3, date1,
isnull(lead(date1) over(partition by id1,id2,id3 order by date1)-1, '99991231') as date2
from test

Try something like this
SELECT t1.id1, t1.id2, t1.id3, t1.date1 as date1,
ISNULL(t2.date1, CONVERT(DATETIME, '12/31/9999 23:59:59.997')) as date2
FROM test t1
LEFT JOIN test t2 ON t1.id1 = t2.id1 AND t1.id2 = t2.id2 AND t1.id3 = t2.id3
AND t1.id < t2.id
ORDER BY t1.id1, t1.id2, t1.id3, date1, date2

Related

SQL Server query Group By Trimester

I'm finding a way to group SQL query by trimester. I have found a way to do it using MySQL on this link.
This is what I'm expecting:
Range Start Range End Count
----------- ---------- -----
2013-09-01 2013-11-26 87
2013-06-01 2013-08-31 92
2013-03-01 2013-05-31 92
2012-12-01 2013-02-28 90
2012-09-01 2012-11-30 91
This is what I have tried:
SELECT MIN(start_date) AS Range_Start, MAX(start_date) AS Range_End, COUNT(ID) AS Total
FROM [dbo].[table]
GROUP BY FLOOR(DATEDIFF(MONTH, DATEADD(DAY, -DAY(start_date)+1, start_date), DATEADD(DAY, -DAY(start_date)+1,getdate())) /3)
ORDER BY 1 ASC
This is what I get:
Range Start Range End Count
----------- ---------- -----
1900-01-01 00:00:00.000 1900-01-01 00:00:00.000 8
1952-01-01 00:00:00.000 1952-01-01 00:00:00.000 2
1954-01-01 00:00:00.000 1954-01-01 00:00:00.000 11
1955-01-01 00:00:00.000 1955-01-01 00:00:00.000 3
1956-01-01 00:00:00.000 1956-01-01 00:00:00.000 2
1957-01-01 00:00:00.000 1957-01-01 00:00:00.000 8
1958-01-01 00:00:00.000 1958-01-01 00:00:00.000 2
1959-01-01 00:00:00.000 1959-01-01 00:00:00.000 5
1960-01-01 00:00:00.000 1960-01-01 00:00:00.000 17
1960-03-17 00:00:00.000 1960-03-17 00:00:00.000 1

How do I join a sparse table and fill rows between in SQL Server

How can I apply weights from a one table to another [Port] where the weight table has sparse dates?
[Port] table
utcDT UsdPnl
-----------------------------------------------
2012-03-09 00:00:00.000 -0.00581815226439161
2012-03-11 00:00:00.000 -0.000535272460588547
2012-03-12 00:00:00.000 -0.00353079778650661
2012-03-13 00:00:00.000 0.00232882689252497
2012-03-14 00:00:00.000 -0.0102592811199384
2012-03-15 00:00:00.000 0.00254451559598693
2012-03-16 00:00:00.000 0.0146718613139845
2012-03-18 00:00:00.000 0.000425144543842752
2012-03-19 00:00:00.000 -0.00388548271428044
2012-03-20 00:00:00.000 -0.00662423680184768
2012-03-21 00:00:00.000 0.00405506208635343
2012-03-22 00:00:00.000 -0.000814822806982203
2012-03-23 00:00:00.000 -0.00289523953346103
2012-03-25 00:00:00.000 0.00204150859774465
2012-03-26 00:00:00.000 -0.00641635182718787
2012-03-27 00:00:00.000 -0.00107168420738448
2012-03-28 00:00:00.000 0.00131000520696153
2012-03-29 00:00:00.000 0.0008223678402638
2012-03-30 00:00:00.000 -0.00255345945390133
2012-04-01 00:00:00.000 -0.00337792814650089
[Weights] table
utcDT Weight
--------------------------------
2012-03-09 00:00:00.000 1
2012-03-20 00:00:00.000 3
2012-03-29 00:00:00.000 7
So, I want to use the weights as if I had a full table like this below. i.e. change to new weight on first day it appears in [Weights] table:
utcDT UsedWeight
----------------------------------
2012-03-09 00:00:00.000 1
2012-03-11 00:00:00.000 1
2012-03-12 00:00:00.000 1
2012-03-13 00:00:00.000 1
2012-03-14 00:00:00.000 1
2012-03-15 00:00:00.000 1
2012-03-16 00:00:00.000 1
2012-03-18 00:00:00.000 1
2012-03-19 00:00:00.000 1
2012-03-20 00:00:00.000 3
2012-03-21 00:00:00.000 3
2012-03-22 00:00:00.000 3
2012-03-23 00:00:00.000 3
2012-03-25 00:00:00.000 3
2012-03-26 00:00:00.000 3
2012-03-27 00:00:00.000 3
2012-03-28 00:00:00.000 3
2012-03-29 00:00:00.000 7
2012-03-30 00:00:00.000 7
2012-04-01 00:00:00.000 7
You can use apply:
select p.*, w.*
from port p outer apply
(select top (1) w.*
from weights w
where w.utcDT <= p.utcDT
order by w.utcDT desc
) w;
outer apply is usually pretty efficient, if you have the right indexes. In this case, the right inex is on weights(utcDT desc).
You can use lead() in a subquery to associate the next date a weight changes to each weights record, and then join with port using an inequality condition on the dates:
select p.utcDt, w.weight
from port p
inner join (
select utcDt, weight, lead(utcDt) over(order by utcDt) lead_utcDt from weights
) w
on p.utcDt >= w.utcDt
and (w.lead_utcDt is null or p.utcDt < w.lead_utcDt)

How to use next date column value to calculate delta for current column

I have a temp table
BusinessDate SSQ_CompScore
2011-01-05 00:00:00.000 41
2011-01-06 00:00:00.000 6
2011-01-07 00:00:00.000 1
2011-01-10 00:00:00.000 8
2011-01-11 00:00:00.000 48
2011-01-12 00:00:00.000 50
2011-01-13 00:00:00.000 59
I need to calculate delta for each current date.
I have prepared a solution but it doesn't work where date as not consecutive.
Can you please help?
select t1.businessdate, t1.ssq_compscore, (t2.ssq_compscore - t1.ssq_compscore) as delta
from #temp t1
left join #temp t2 on t1.businessdate = DATEADD(dd,1,t2.businessdate)
where t1.businessdate >='20180814'
Result set should be as
BusinessDate SSQ_CompScore Delta
2011-01-05 00:00:00.000 41 NULL
2011-01-06 00:00:00.000 6 35
2011-01-07 00:00:00.000 1 5
2011-01-10 00:00:00.000 8 7
2011-01-11 00:00:00.000 48 40
2011-01-12 00:00:00.000 50 2
2011-01-13 00:00:00.000 59 9
Not sure if this is the most efficient way but it works as far as I see
SELECT businessdate, SSQ_CompScore ,
SSQ_CompScore - (SELECT SSQ_CompScore
FROM temp
WHERE businessdate < t1.businessdate
ORDER BY businessdate DESC
LIMIT 1) as delta
FROM temp t1
ORDER BY businessdate ASC

SQL Server : compare rows, exclude from results when some values are the same

I have the following SQL Server query problem.
If there is a row where Issue_DATE = as Maturity_Date in another row, and if both rows have the same ID and Amount USD, then none of these rows should be displayed.
Here is a simplified version of my table:
ID ISSUE_DATE MATURITY_DATE AMOUNT_USD
1 2010-01-01 00:00:00.000 2015-12-01 00:00:00.000 5000
1 2010-01-01 00:00:00.000 2001-09-19 00:00:00.000 700
2 2014-04-09 00:00:00.000 2019-04-09 00:00:00.000 400
1 2015-12-01 00:00:00.000 2016-12-31 00:00:00.000 5000
5 2015-02-24 00:00:00.000 2015-02-24 00:00:00.000 8000
4 2012-11-29 00:00:00.000 2015-11-29 00:00:00.000 10000
3 2015-01-21 00:00:00.000 2018-01-21 00:00:00.000 17500
2 2015-02-02 00:00:00.000 2015-12-05 00:00:00.000 12000
1 2015-01-12 00:00:00.000 2018-01-12 00:00:00.000 18000
2 2015-12-05 00:00:00.000 2016-01-10 00:00:00.000 12000
Result should be:
ID ISSUE_DATE MATURITY_DATE AMOUNT_USD
1 2010-01-01 00:00:00.000 2001-09-19 00:00:00.000 700
2 2014-04-09 00:00:00.000 2019-04-09 00:00:00.000 400
5 2015-02-24 00:00:00.000 2015-02-24 00:00:00.000 8000
4 2012-11-29 00:00:00.000 2015-11-29 00:00:00.000 10000
3 2015-01-21 00:00:00.000 2018-01-21 00:00:00.000 17500
1 2015-01-12 00:00:00.000 2018-01-12 00:00:00.000 18000
I tried with self join, but I do not get right result.
Thanks in advance!
Can you try something like this? 'not exists' is the way of doing it.
select * from table t1 where not exists (select 'x' from table t2 where t1.issue_date = t2.maturity_date and t1.amount_usd=t2.amount_usd and t1.id = t2.id)
I'd think about making subquery of all the dupes and then eliminating them from the first table like so:
select t1.ID
, t1.ISSUE_DATE
, t1.MATURITY_DATE
, t1.AMOUNT_USD
FROM
t1
LEFT JOIN
(select a.ID
, a.ISSUE_DATE
, a.MATURITY_DATE
, a.AMOUNT_USD
FROM
t1 a
INNER JOIN
ti b
) dupes
on
t1.ID = dupes.ID
WHERE dupes.ID IS NULL;

SQL Server query join several tables

I have a query that I don't think should be that hard to make, however, I've spent a lot of time on it now and still can't get it the way I want, so I hope someone here can help me.
Basically, I need to create a report that will give a value for each month, for each area. However, not all areas deliver data each month; in that case the view should return NULL for that month and area. So, the view need to look something like this:
Month Area Value
2012-08-01 Area1 2
2012-08-01 Area2 3
2012-09-01 Area1 3
2012-09-01 Area2 NULL
My data table looks something like this
Date Area Value
2012-08-01 Area1 2
2012-08-01 Area2 3
2012-09-01 Area1 3 -- Notice that Area2 is not present for September here
I have a table with all the available areas
Furthermore, I have created a table-valued function that returns all dates from a given date until now.
For example this statement
SELECT * FROM Periods_Months('2012-01-01')
would return 8 records like:
DateValue Year Month YearMonth
2012-01-01 00:00:00.000 2012 1 20121
2012-02-01 00:00:00.000 2012 2 20122
2012-03-01 00:00:00.000 2012 3 20123
2012-04-01 00:00:00.000 2012 4 20124
2012-05-01 00:00:00.000 2012 5 20125
2012-06-01 00:00:00.000 2012 6 20126
2012-07-01 00:00:00.000 2012 7 20127
2012-08-01 00:00:00.000 2012 8 20128
Based on the suggestions, my query now looks like this:
WITH months AS (
SELECT DateValue, YearMonth FROM Periods_Months('2011-01-01')
)
select m.DateValue
,CAST(DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,m.DateValue)+1,0)) AS Date) AS DateReported -- Get last day in month
,ResponseTime AS Value
,g.ExternalId
from GISDB.dbo.GisObjects g
CROSS JOIN months m
LEFT OUTER JOIN
( -- SELECT data from data table, grouped by area and month
SELECT dbo.YearMonth(CloseDate) AS YearMonth
,MAX(CloseDate) AS LastDate
,GisObjectId
,SUM(DATEDIFF(HH,RegDate,CloseDate)) AS ResponseTime -- calculate response time between start and end data (the value we need)
FROM DataTable
WHERE CloseDate IS NOT NULL
AND GisObjectId IS NOT NULL
GROUP BY GisObjectId, dbo.YearMonth(CloseDate) -- group by area and month
) c
ON g.ObjectId = c.GisObjectId AND c.YearMonth = m.YearMonth
WHERE g.CompanyId = 3 AND g.ObjectTypeId = 1 -- reduce the GIS objects that we compare to
ORDER BY m.DateValue, g.ObjectId
But the result is this (Value is always NULL):
DateValue DateReported Value ExternalId
2011-01-01 00:00:00.000 31-01-2011 NULL 9994
2011-01-01 00:00:00.000 31-01-2011 NULL 9993
2011-01-01 00:00:00.000 31-01-2011 NULL 9992
2011-01-01 00:00:00.000 31-01-2011 NULL 9991
2011-01-01 00:00:00.000 31-01-2011 NULL 2339
2011-01-01 00:00:00.000 31-01-2011 NULL 2338
2011-01-01 00:00:00.000 31-01-2011 NULL 2337
2011-01-01 00:00:00.000 31-01-2011 NULL 2336
2011-01-01 00:00:00.000 31-01-2011 NULL 2335
2011-01-01 00:00:00.000 31-01-2011 NULL 2334
2011-01-01 00:00:00.000 31-01-2011 NULL 2327
2011-01-01 00:00:00.000 31-01-2011 NULL 2326
2011-01-01 00:00:00.000 31-01-2011 NULL 2325
2011-01-01 00:00:00.000 31-01-2011 NULL 2324
2011-01-01 00:00:00.000 31-01-2011 NULL 2323
2011-01-01 00:00:00.000 31-01-2011 NULL 2322
etc.
I suppose you have a table with all your areas, which I call area_table.
WITH month_table AS (
SELECT dateValue FROM Periods_Months('2012-01-01')
)
select * from area_table
CROSS JOIN month_table
LEFT OUTER JOIN myValueTable
ON area_table.name = myValueTable.area
AND myValueTable.date = left(convert(varchar(30),month_table.dateValue,120),10)
ORDER BY myValueTable.Month, myValueTable.area
Suppose Areas is your table for all available areas, t - is your data table:
SELECT pm.dateValue,Ar.Area, t.value
FROM Periods_Months('2012-01-01') pm, Areas ar
left join t on (pm.dateValue=t.Date) and (ar.Area=t.Area)
order by pm.DateValue,ar.Area