getting distinct rows based on two column values - tsql

I am trying to get distinct rows from a temporary table and output them to an aspx page. I am trying to use the value of one column and get the last entry made into that column.
I have been trying to use inner join and max(). However i have been unsuccessful.
Here is the code i have been trying to do it with.
Declare #TempTable table (
viewIcon nvarchar(10),
tenderType nvarchar(20),
diaryIcon int,
customerName nvarchar(100),
projectName nvarchar(100),
diaryEntry nvarchar(max),
diaryDate nvarchar(20),
pid nvarchar(20)
)
insert into #TempTable(
viewIcon,
tenderType,
diaryIcon,
customerName,
projectName,
diaryEntry ,
diaryDate ,
pid
)
select p.viewicon,
p.[Tender Type],
1 diaryicon,
c.[Customer Name],
co.[Last Project],
d.Action,
co.[Diary Date],
p.PID
From Projects2 p Inner Join
(select distinct Pno, max(convert(date,[date of next call],103)) maxdate from ProjectDiary group by Pno
) td on p.PID = td.Pno
Inner Join contacts3 co on co.[Customer Number] = p.[Customer Number]
Inner Join Customers3 c on p.[Customer Number] = c.[Customer Number]
Inner Join ProjectDiary d on td.Pno = d.Pno
Where CONVERT(Date, co.[Diary Date], 103) BETWEEN GETDATE()-120 AND GETDATE()-60
DECLARE #contactsTable TABLE
(pid nvarchar(200),
diaryDate date)
insert into #contactsTable (t.pid, t.diarydate)
select distinct pid as pid, MAX(CONVERT(DATE, diaryDate, 103)) as diaryDate from # TempTable t group by pid
DECLARE #tempContacts TABLE
(pid nvarchar(200))
insert into #tempContacts(pid)
select pid from #contactsTable
DECLARE #tempDiaryDate TABLE (diaryDate date)
insert into #tempDiaryDate(diaryDate)
select distinct MAX(CONVERT(DATE, diaryDate, 103)) from #TempTable
select t.* from #TempTable t inner join (select distinct customerName, M AX(CONVERT(DATE, diaryDate, 103)) AS diaryDate from #TempTable group by customerName) tt on t t.customerName=t.customerName
where t.pid not in
(select Pno from ProjectDiary where convert(date,[Date Of Next Call],103) > GETDATE())
and t.viewIcon <> '098'
and t.viewIcon <> '163'
and t.viewIcon <> '119'
and t.pid in (select distinct pid from #tempContacts)
and CONVERT(DATE, t.diaryDate, 103) in (select distinct CONVERT(DATE, diaryDate, 103) f rom #tempDiaryDate)
order by CONVERT(DATE, tt.diaryDate, 103)
I am trying to get all the distinct customerName's using the max date to determine which record it uses.

Use a subquery. Without going through your entire sql statement, the general idea is:
Select [Stuff]
From table t
Where date = (Select Max(Date) from table
where customer = t.customer)

Related

Create New Rows based on valid to and valid from dates

I have a table that has account number, end of month valid from and end of month valid to columns.
What I need is a table that has account number and a column that has all the end of month dates of when the account was live, inclusive of end of month valid to. The Current Table looks like this
New table will need to be like this
I have tried using a calendar table and an CTE table type query but have had no success.
Any help would be great.
This can be achieved using Using multiple comma separated CTEs in a statement
Query
with t0 (i) AS (select 0 union all select 0 union all select 0 union all select 0 union all select 0 union all select 0),
t1 (i) AS (select 0 from t0 a inner join t0 b on a.i = b.i),
n (i) AS (select row_number()over(order by i) from t1),
Account_details (Account_number,valid_from,valid_to,mth,Live_date)As(
select Account_number,valid_from,valid_to, datediff(month,valid_from,valid_to ) mth, valid_from"Live_date"
from tbl1
union all
select Account_number,valid_from,valid_to, datediff(month,valid_from,valid_to ) mth, EOMONTH (dateadd(month,n.i,valid_from)) "Live_date"
from tbl1
inner join n on 1=1 and n.i between 1 and datediff(month,valid_from,valid_to )
)
select *
from Account_details
where Account_details.Account_number =1
order by Account_details.Account_number
Output
CTE Table t0, t1 and n will generate numbers. This is a best way to generate rows without any data.
Then the CTE table Account_details is used to pull data from the table.
Based on sql on the msdn thread how to get month end date between two dates.
DECLARE #Old AS Table (AccountNumber INT, ValidFrom DATE, ValidTo DATE)
DECLARE #New AS Table (AccountNumber INT, LiveDate DATE)
INSERT INTO #old
SELECT 1, '20130630', '20131130' UNION ALL
SELECT 2, '20130630', '20131231' UNION ALL
SELECT 3, '20120430', '20120531' UNION ALL
SELECT 4, '20170331', '20171130'
SELECT TOP 100 * FROM #old
DECLARE #AccountNumber INT, #ValidFrom DATE, #ValidTo DATE
DECLARE #Cursor CURSOR
SET #Cursor = CURSOR FOR
SELECT AccountNumber, ValidFrom, ValidTo
FROM #old
OPEN #Cursor
FETCH NEXT INTO #Cursor FROM #AccountNumber, #ValidFrom, #ValidTo
WHILE ##FETCH_STATUS = 0
BEGIN
;WITH cteEndMonthDates (MonthEndDate)
AS
(
SELECT eomonth(#ValidFrom) AS MonthEndDate
UNION ALL
SELECT eomonth( dateadd(day, 1, MonthEndDate)) AS MonthEndDate
FROM cteEndMonthDates
WHERE MonthEndDate < eomonth(#ValidTo)
)
INSERT INTO #new (AccountNumber, LiveDate)
SELECT #AccountNumber, MonthEndDate
FROM cteEndMonthDates
FETCH NEXT FROM #Cursor INTO #AccountNumber, #ValidFrom, #ValidTo
END
CLOSE #Cursor
DEALLOCATE #Cursor
SELECT * FROM #New
Edit: Or without the cursor
DECLARE #Old AS Table (AccountNumber INT, ValidFrom DATE, ValidTo DATE)
DECLARE #New AS Table (AccountNumber INT, LiveDate DATE)
INSERT INTO #old
SELECT 1, '20130630', '20131130' UNION ALL
SELECT 2, '20130630', '20131231' UNION ALL
SELECT 3, '20120430', '20120531' UNION ALL
SELECT 4, '20170331', '20171130' UNION ALL
SELECT 5, '20180430', '20190131' UNION ALL
SELECT 6, '20160430', '20180531'
SELECT TOP 100 * FROM #old
;WITH cteEndMonthDates (AccountNumber, MonthEndDate)
AS
(
SELECT AccountNumber, eomonth(ValidFrom) AS MonthEndDate
FROM #Old
UNION ALL
SELECT x.AccountNumber, eomonth( dateadd(day, 1, MonthEndDate)) AS MonthEndDate
FROM cteEndMonthDates x
JOIN #Old o ON o.AccountNumber = x.AccountNumber
WHERE MonthEndDate < eomonth(ValidTo)
)
SELECT AccountNumber, MonthEndDate
FROM cteEndMonthDates
order by AccountNumber, MonthEndDate
This should work.
;WITH Span AS (
SELECT
AccountNumber,
ValidFrom AS Valid
FROM dbo.Input
UNION ALL
SELECT
AccountNumber,
DATEADD(DAY, 1, Span.Valid) AS Valid
FROM Span
WHERE DATEADD(DAY, 1, Span.Valid) <= (SELECT ValidTo FROM dbo.Input WHERE AccountNumber = Span.AccountNumber)
)
SELECT * FROM Span
ORDER BY Span.AccountNumber, Span.Valid
OPTION (MAXRECURSION 0);

TSQL -- quick way to get a count across all tables

If database d1 has tables T1,T2,T3,T4 all with the field "Date1".
What is the best way to get a count of all records across all tables with a date older than 3 days ago?
I know one could do unions, I assume there is no nifty syntax that would omit all tables [like a 'parent' object in C++].
Here best may mean more efficient, or just a pleasing syntax in T-SQL.
This is for SSMS 17.7. Microsoft SQL Server 2014 (SP2)
If you know the table names in advance, a simple query on union all will probably be the simplest way:
SELECT COUNT(*)
FROM
(
SELECT Date1
FROM T1
UNION ALL
SELECT Date1
FROM T2
SELECT Date1
FROM T3
SELECT Date1
FROM T4
) As t
WHERE Date1 <= DATEADD(DAY, -3, GETDATE())
If you don't know the table names in advance, you can use information_schema.columns to build the union query dynamically.
Well, you're interested in a parent object, that would be a view, then. You can reuse it for a variety of queries. Alternatively, add more columns if you need them:
CREATE VIEW parent AS
SELECT Date1 FROM t1 UNION ALL
SELECT Date1 FROM t2 UNION ALL
SELECT Date1 FROM t3 UNION ALL
SELECT Date1 FROM t4;
And now, that can be queried in the way you want
SELECT COUNT(*) FROM parent WHERE Date1 <= DATEADD(DAY, -3, GETDATE())
Without UNION?
Since a COUNT without a GROUP BY returns 1 value, why not use CROSS JOIN for once?
SELECT
t1.Cnt AS [T1],
t2.Cnt AS [T2],
t3.Cnt AS [T3],
t4.Cnt AS [T4],
(t1.Cnt + t2.Cnt + t3.Cnt + t4.Cnt) AS [T1234]
FROM
(SELECT COUNT(*) AS Cnt FROM T1 WHERE [Date1] < CAST(GetDate()-3 AS DATE)) AS t1
CROSS JOIN
(SELECT COUNT(*) AS Cnt FROM T2 WHERE [Date1] < CAST(GetDate()-3 AS DATE)) AS t2
CROSS JOIN
(SELECT COUNT(*) AS Cnt FROM T3 WHERE [Date1] < CAST(GetDate()-3 AS DATE)) AS t3
CROSS JOIN
(SELECT COUNT(*) AS Cnt FROM T4 WHERE [Date1] < CAST(GetDate()-3 AS DATE)) AS t4
Or a CROSS APPLY
SELECT
t1.Cnt AS [T1],
t2.Cnt AS [T2],
t3.Cnt AS [T3],
t4.Cnt AS [T4],
(t1.Cnt + t2.Cnt + t3.Cnt + t4.Cnt) AS [T1234]
FROM (SELECT CAST(GetDate()-3 AS DATE) as Dt) d
CROSS APPLY (SELECT COUNT(*) AS Cnt FROM T1 WHERE [Date1] < d.Dt) AS t1
CROSS APPLY (SELECT COUNT(*) AS Cnt FROM T2 WHERE [Date1] < d.Dt) AS t2
CROSS APPLY (SELECT COUNT(*) AS Cnt FROM T3 WHERE [Date1] < d.Dt) AS t3
CROSS APPLY (SELECT COUNT(*) AS Cnt FROM T4 WHERE [Date1] < d.Dt) AS t4
Example snippet for Sql Server:
declare #T1 table (id int primary key identity(1,1), [Date1] date);
declare #T2 table (id int primary key identity(1,1), [Date1] date);
declare #T3 table (id int primary key identity(1,1), [Date1] date);
declare #T4 table (id int primary key identity(1,1), [Date1] date);
insert into #T1 ([Date1]) values (getdate()-6),(getdate()-5),(getdate()-4),(getdate()-3),(getdate()-2),(getdate()-1),(getdate()-0);
insert into #T2 ([Date1]) select top 6 [Date1] from #T1 order by [Date1] desc;
insert into #T3 ([Date1]) select top 5 [Date1] from #T1 order by [Date1] desc;
insert into #T4 ([Date1]) select top 4 [Date1] from #T1 order by [Date1] desc;
SELECT
t1.Cnt AS [T1],
t2.Cnt AS [T2],
t3.Cnt AS [T3],
t4.Cnt AS [T4],
(t1.Cnt + t2.Cnt + t3.Cnt + t4.Cnt) AS [T1234]
FROM
(SELECT COUNT(*) AS Cnt FROM #T1 WHERE [Date1] < CAST(GetDate()-3 AS DATE)) AS t1
CROSS JOIN
(SELECT COUNT(*) AS Cnt FROM #T2 WHERE [Date1] < CAST(GetDate()-3 AS DATE)) AS t2
CROSS JOIN
(SELECT COUNT(*) AS Cnt FROM #T3 WHERE [Date1] < CAST(GetDate()-3 AS DATE)) AS t3
CROSS JOIN
(SELECT COUNT(*) AS Cnt FROM #T4 WHERE [Date1] < CAST(GetDate()-3 AS DATE)) AS t4
Returns:
T1 T2 T3 T4 T1234
3 2 1 0 6
Instead create a view, you can use a CTE (Common Table Expression). It works like a view, but not persists on database. Please try it:
WITH CteDate( Date1 )
AS ( SELECT Date1 FROM t1 UNION ALL
SELECT Date1 FROM t2 UNION ALL
SELECT Date1 FROM t3 UNION ALL
SELECT Date1 FROM t4
)
SELECT COUNT(*) FROM CteDate WHERE Date1 <= DATEADD(DAY, -3, GETDATE())
It works for all SQL Server greater or equal then 2005.

TSQL- Calculation with previous record in SQL Server 2008 R2

-- Create Sample data
Use AdventureWorks2012;
IF OBJECT_ID('TempTable1', 'U') IS NOT NULL
DROP TABLE TempTable1
-- Grab data from AdventureWorks2012
SELECT SOD.ProductId as ResourceID,
SOH.DueDate as DueDate,
SOD.OrderQty as DayIncrement,
Row_Number() over (PARTITION BY SOD.ProductID ORDER By SOH.DueDate) as JResourceNumber
INTO TempTable1
FROM Sales.SalesOrderHeader SOH
INNER JOIN Sales.SalesOrderDetail SOD
ON SOH.SalesOrderID = SOD.SalesOrderID
ORDER by ResourceID, JResourceNumber
-- Reduce records to 2637
IF OBJECT_ID('TempTable', 'U') IS NOT NULL
DROP TABLE TempTable
SELECT ResourceID, DueDate, DayIncrement, Row_Number() over (ORDER By ResourceID) as JRowNumber, GETDATE() as SchedDate
INTO TempTable
FROM TempTable1
WHERE JResourceNumber <= 10
--END create sample data
--
-- Calulate a rolling available date for ResourceId = Prev.SchedDate + Curr.DayIncrement
--
IF OBJECT_ID('FINALTABLE', 'U') IS NOT NULL
DROP TABLE FinalTable
SELECT Curr.ResourceID, Curr.DueDate, Curr.DayIncrement, Curr.JRowNumber as CurrRowNumber, Prev.JRowNumber as PrevJRowNumber,
Prev.SchedDate as ShouldBePrevSchedDate,
Case
WHEN Curr.ResourceID = Prev.ResourceID THEN DATEADD(DAY, Curr.DayIncrement, Prev.SchedDate)
ELSE GETDATE()
END AS SchedDate
-- This is the self join
FROM TempTable Curr
LEFT JOIN TempTable Prev ON Prev.JRowNumber = Curr.JRowNumber - 1

SQL Server : group by with corresponding row values

I need to write a T-SQL group by query for a table with multiple dates and seq columns:
DROP TABLE #temp
CREATE TABLE #temp(
id char(1),
dt DateTime,
seq int)
Insert into #temp values('A','2015-03-31 10:00:00',1)
Insert into #temp values('A','2015-08-31 10:00:00',2)
Insert into #temp values('A','2015-03-31 10:00:00',5)
Insert into #temp values('B','2015-09-01 10:00:00',1)
Insert into #temp values('B','2015-09-01 10:00:00',2)
I want the results to contains only the items A,B with their latest date and the corresponding seq number, like:
id MaxDate CorrespondentSeq
A 2015-08-31 10:00:00.000 2
B 2015-09-01 10:00:00.000 2
I am trying with (the obviously wrong!):
select id, max(dt) as MaxDate, max(seq) as CorrespondentSeq
from #temp
group by id
which returns:
id MaxDate CorrespondentSeq
A 2015-08-31 10:00:00.000 5 <-- 5 is wrong
B 2015-09-01 10:00:00.000 2
How can I achieve that?
EDIT
The dt datetime column has duplicated values (exactly same date!)
I am using SQL Server 2005
You can use a ranking subselect to get only the highest ranked entries for an id:
select id, dt, seq
from (
select id, dt, seq, rank() over (partition by id order by dt desc, seq desc) as r
from #temp
) ranked
where r=1;
SELECT ID, DT, SEQ
FROM (
SELECT ID, DT, SEQ, Row_Number()
OVER (PARTITION BY id ORDER BY dt DESC, seq DESC) AS row_number
FROM temp
) cte
WHERE row_number = 1;
Demo : http://www.sqlfiddle.com/#!3/3e3d5/5
With trial and errors maybe I have found a solution, but I'm not completely sure this is correct:
select A.id, B.dt, max(B.seq)
from (select id, max(dt) as maxDt
from #temp
group by id) as A
inner join #temp as B on A.id = B.id AND A.maxDt = B.dt
group by A.id, B.dt
Select id, dt, seq
From #temp t
where dt = (Select Max(dt) from #temp
Where id = t.Id)
If there are duplicate rows, then you also need to specify what the query processor should use to determine which of the duplicates to return. Say you want the lowest value of seq,
Then you could write:
Select id, dt, seq
From #temp t
where dt = (Select Max(dt) from #temp
Where id = t.Id)
and seq = (Select Min(Seq) from #temp
where id = t.Id
and dt = t.dt)

DATEDIFF On Dates

I've been struggling with this but I'm not really good at tsql.
This is what I got, and I can't have the DateTime calculates all right. I'm getting the sum between A and B but not the total sum. For example in the last column I have a 0 which is getting me back to -x.
Here is the procedure, and some of the data are like this:
Code_Procedure date_evenement codes_situation
---------------------------------------------------------------
000079500000 2013-05-21 13:07:00.000 COMCFM
000079500000 2013-05-21 20:24:00.000 PCHCFM
000079500000 2013-05-22 09:58:00.000 PCHCFM
000079500000 2013-05-23 00:00:00.000 AARCFM
000079500000 2013-05-23 00:00:00.000 LIVCFM
000079600000 2013-05-21 13:07:00.000 COMCFM
000079600000 2013-05-21 20:24:00.000 PCHCFM
000079600000 2013-05-22 11:18:00.000 PCHCFM
000079600000 2013-05-23 00:00:00.000 AARCFM
000079600000 2013-05-23 00:00:00.000 LIVCFM
Here is the proc:
DECLARE #COMCFM TABLE(numero_colis VARCHAR(25), date_evenement DATETIME);
INSERT #COMCFM SELECT TOP(5) numero_colis, date_evenement FROM cartitem_colis_postaux_etats WHERE (code_situation = 'PCH' AND code_justification = 'CFM')
WHILE (SELECT COUNT(*) FROM #COMCFM) > 0
BEGIN
DECLARE #Colis TABLE(numero_colis VARCHAR(25), date_evenement DATETIME, code_situation_code_justification NVARCHAR(32));
INSERT #Colis SELECT numero_colis, date_evenement, code_situation + code_justification FROM cartitem_colis_postaux_etats WHERE numero_colis = (SELECT TOP(1) numero_colis FROM #COMCFM) ORDER BY numero_colis, date_evenement
;WITH CTE AS
(
Select DISTINCT
*
,ROW_NUMBER() OVER(PARTITION BY numero_colis ORDER BY date_evenement ASC) Rn FROM #Colis
),CTE1 AS
(
SELECT DISTINCT
A.*
,DATEDIFF(mi, B.date_evenement, A.date_evenement) AS DIFF
FROM CTE A INNER JOIN CTE B On B.Rn + 1 = A.Rn
UNION All
SELECT A.*, 0 FROM CTE A Where Rn = 1
)
SELECT
A.*
,ISNULL((
SELECT
A.DIFF + B.DIFF
FROM CTE1 AS B
WHERE A.numero_colis = B.numero_colis
AND A.Rn = B.Rn + 1), 0) AS Sums
FROM CTE1 AS a
ORDER BY numero_colis, Rn ASC
DELETE FROM #Colis
DELETE FROM #COMCFM WHERE numero_colis = (SELECT TOP(1) numero_colis FROM #COMCFM)
END
I'm not really sure what you would like to achieve. Do you need date differencies as a cummulated value? If you need this, change your BEGIN-END block of your stored procedure with this code
BEGIN
DECLARE #Colis TABLE(numero_colis VARCHAR(25), date_evenement DATETIME, code_situation_code_justification NVARCHAR(32));
INSERT #Colis SELECT numero_colis, date_evenement, code_situation + code_justification FROM cartitem_colis_postaux_etats WHERE numero_colis = (SELECT TOP(1) numero_colis FROM #COMCFM) ORDER BY numero_colis, date_evenement
;WITH CTE AS
(
SELECT DISTINCT
*,
ROW_NUMBER() OVER(PARTITION BY numero_colis ORDER BY date_evenement ASC) Rn
FROM #Colis
),CTE1 AS
(
SELECT A.*, 0 AS CummulatedDiff
FROM CTE A
WHERE Rn = 1
UNION ALL
SELECT DISTINCT A.*, B.CummulatedDiff + DATEDIFF(mi, B.date_evenement, A.date_evenement) AS CummulatedDiff
FROM CTE AS A INNER JOIN
CTE1 AS B ON B.Rn + 1 = A.Rn AND B.numero_colis = A.numero_colis
)
SELECT *
FROM CTE1 AS a
ORDER BY numero_colis, Rn ASC
DELETE FROM #Colis
DELETE FROM #COMCFM WHERE numero_colis = (SELECT TOP(1) numero_colis FROM #COMCFM)
END
I hope this takes you further to your goal.