I have the result of my query in a temp table which looks something like shown below :
CREATE TABLE #temptable
(
productid INT,
date DATE,
Indicator varchar(max),
VendorCode INT,
morning INT,
noon INT,
evening INT
)
insert into #temptable values (101,'8-5-2016', 'High', 202, 0,1,0)
insert into #temptable values (101,'8-6-2016', 'High', 202, 0,0,1)
insert into #temptable values (101,'8-5-2016', 'Low', 202, 0,0,1)
insert into #temptable values (101,'8-6-2016', 'Low', 202, 0,0,1)
insert into #temptable values (101,'8-5-2016', 'Avg', 202, 1,0,1)
insert into #temptable values (101,'8-6-2016', 'Avg', 202, 0,0,1)
select * from #temptable
I need the output to look something like this :
I looked at using pivots but looks like that works only with aggregates ? Is there an easy way to do this ?
You can get the result you want by first applying the unpivot operator, and then pivot the result:
select
productid, VendorCode, date, time, Low, High, Avg
from (
select productid, VendorCode, date, time, Indicator, val
from #temptable
unpivot (val for time in ([morning],[noon],[evening])) u
) t
pivot (max(val) for indicator in ([Low],[High],[Avg])) p
order by
productid, VendorCode, date,
case time
when 'Morning' then 1
when 'Noon' then 2
when 'Evening' then 3
end
The case expression at the end of the order by clause makes sure the result is ordered correctly (Morning, Noon, Evening).
First do UNPIVOT then PIVOT
SELECT
piv.productid ,
piv.date ,
piv.VendorCode ,
piv.Tmp AS [Time],
piv.Low ,
piv.High ,
piv.Avg
from
(
select *
from #temptable
unpivot
(
[Time]
for [Tmp] in (morning, noon, evening)
) u
) src
pivot
(
min([Time])
for Indicator in ([Low], [High], [Avg])
) piv
Result:
productid date VendorCode Time Low High Avg
101 2016-08-05 202 evening 1 0 1
101 2016-08-05 202 morning 0 0 1
101 2016-08-05 202 noon 0 1 0
101 2016-08-06 202 evening 1 1 1
101 2016-08-06 202 morning 0 0 0
101 2016-08-06 202 noon 0 0 0
Related
I have the below data in a SQL Server 2017 table:
POS_ID Term Code Status IsActive
----------------------------------------------------
TR 101 In Progress true
TR 102 In Progress true
TR 103 In Progress true
CA 151 In Progress true
CA 152 In Progress true
DA 161 In Progress true
The requirement is I want to iterate each row and compare the current row POS_ID with the previous row and if there is a different POS_ID found then I want to insert 2 rows in between in such a way that the 1st inserted row's status for that previous POS_ID will be "In Progress" and true and 2nd inserted row's status will be discontinued and false and term code of both these 2 inserted rows will be Term code of new POS_ID of current row.
Something like in this example:
POS_ID Term Code Status IsActive
---------------------------------------------------------
TR 101 In Progress true
TR 102 In Progress true
TR 103 In Progress true
TR 151 In Progress true -- NEW ROW
TR 151 discontinue false -- NEW ROW
CA 151 In Progress true
CA 152 In Progress true
CA 161 In Progress true -- NEW ROW
CA 161 discontinue false -- NEW ROW
DA 161 In Progress true
I have tried using the Lead function to check the next value in the row for the column, but not sure how to do implement logic as mentioned above if values don't match insert 2 new rows
SELECT
POS_ID AS currentvalue,
LEAD(POS_ID) OVER (ORDER BY uniqueid) AS NextValue
FROM
dbo.input_Main_data
Create table and insert script
CREATE TABLE dbo.input_Main_data
(
UniqueId bigint identity(1,1),
POS_ID varchar(10),
Term_code bigint,
Status varchar(50),
IsActive bit,
CONSTRAINT PK_input_Main_data_UniqueId
PRIMARY KEY (UniqueId)
)
GO
INSERT INTO dbo.input_Main_data([POS_ID], [Term_code], [Status], [IsActive])
VALUES ('TR', 101, 'IN_PROGRESS', 1), ('TR', 102, 'IN_PROGRESS', 1),
('TR', 103, 'IN_PROGRESS', 1), ('CA', 151, 'IN_PROGRESS', 1),
('CA', 152, 'IN_PROGRESS', 1), ('DA', 161, 'IN_PROGRESS', 1)
Combining the lead() and lag() functions with a common table expression (CTE), some case expressions and an insert statement should do it.
Sample data
create table MyData
(
uniqueid bigint IDENTITY(1,1),
POS_ID nvarchar(2),
Term_Code int,
Status nvarchar(15),
IsActive bit
);
insert into MyData (POS_ID, Term_Code, Status, IsActive) values
('TR', 101, 'In Progress', 1),
('TR', 102, 'In Progress', 1),
('TR', 103, 'In Progress', 1),
('CA', 151, 'In Progress', 1),
('CA', 152, 'In Progress', 1),
('DA', 161, 'In Progress', 1);
Solution
with cte as
(
select md.*,
coalesce(lead(md.POS_ID) over(order by md.uniqueid), md.POS_ID) as prev_POS_ID,
coalesce( lag(md.POS_ID) over(order by md.uniqueid), md.POS_ID) as next_POS_ID,
coalesce(lead(md.Term_Code) over(order by md.uniqueid), md.Term_Code) as prev_Term_Code
from MyData md
)
insert into MyData (POS_ID, Term_Code, Status, IsActive)
select c.next_POS_ID as POS_ID,
case when c.POS_ID <> c.prev_POS_ID then c.prev_Term_Code else c.Term_Code end as Term_Code,
case when c.POS_ID <> c.prev_POS_ID then c.Status else 'discontinue' end as Status,
case when c.POS_ID <> c.prev_POS_ID then c.IsActive else 0 end as IsActive
from cte c
where c.POS_ID <> c.prev_POS_ID
or c.POS_ID <> c.next_POS_ID;
Result
POS_ID Term_Code Status IsActive
------ --------- ----------- --------
TR 101 In Progress True
TR 102 In Progress True
TR 103 In Progress True
TR 151 In Progress True
TR 151 discontinue False
CA 151 In Progress True
CA 152 In Progress True
CA 161 In Progress True
CA 161 discontinue False
DA 161 In Progress True
Fiddle with intermediate CTE results and step by step construction.
The new rows to be inserted are based on the lag(pos_id) ordered by term_code. Where the lag(pos_id)<>pos_id then insert 2x rows.
Using the sample data provided (as a temp table)
drop table if exists #input_Main_data;
go
Create table #input_Main_data
( UniqueId bigint identity(1,1),
POS_ID varchar(10),
Term_code bigint,
Status varchar(50),
IsActive bit,
CONSTRAINT PK_input_Main_data_UniqueId PRIMARY KEY (UniqueId));
go
INSERT INTO #input_Main_data([POS_ID],[Term_code], [Status], [IsActive]) Values
('TR',101,'IN_PROGRESS',1), ('TR',102,'IN_PROGRESS',1), ('TR',103,'IN_PROGRESS',1),
('CA',151,'IN_PROGRESS',1),('CA',152,'IN_PROGRESS',1),('DA',161,'IN_PROGRESS',1);
Insert statment
with lag_cte as (
select *, lag(pos_id) over (order by term_code) lag_pos
from #input_Main_data)
insert #input_Main_data
select lag_pos, term_code, [status], isactive
from lag_cte
where pos_id<>lag_pos
union all
select lag_pos, term_code, 'discontinue', 0
from lag_cte
where pos_id<>lag_pos;
Query (after INSERT statement)
select [POS_ID],[Term_code], [Status], [IsActive]
from #input_Main_data
order by pos_id desc, term_code, isactive desc;
Output
POS_ID Term_code Status IsActive
TR 101 IN_PROGRESS 1
TR 102 IN_PROGRESS 1
TR 103 IN_PROGRESS 1
TR 151 IN_PROGRESS 1
TR 151 discontinue 0
DA 161 IN_PROGRESS 1
CA 151 IN_PROGRESS 1
CA 152 IN_PROGRESS 1
CA 161 IN_PROGRESS 1
CA 161 discontinue 0
I think this can help :
SELECT [POS_ID],[Term Code],[Status],IsActive,[Priority] from(
select [UniqueId]
,[POS_ID]
,[Term Code]
,c.[Status]
,IsActive
,Last_Pos_ID
,c.[Priority]
from(
Select * from(
SELECT [UniqueId]
,[POS_ID]
,[Term Code]
,[Status]
,IsActive
,LAG([POS_ID],1) over(order by [UniqueId]) Last_Pos_ID
FROM dbo.input_Main_data) tmp
where Last_Pos_ID is not null and Last_Pos_ID <>[POS_ID] )b
cross apply
(
select 'In Progress' [Status],0 [Priority]
union
select 'discontinue' [Status],1 [Priority]
) c
) d
order by [UniqueId],[Priority]
This script jus generate new rows if you want you can union it by your data
the result is some think like this
Thanks, #SteveC and #Sander I used a combination of queries that were shared by both of you to generate the final result.
Sharing the final query here, so it might be useful for anyone with a similar requirement in the future.
;with lag_cte as (
select *, lag(pos_id) over (order by term_code) lag_pos
from dbo.input_Main_data)
insert dbo.input_Main_data
select lag_pos, term_code, [status], isactive
from lag_cte
where pos_id<>lag_pos
union all
select lag_pos, term_code, 'discontinue', 0
from lag_cte
where pos_id<>lag_pos;
-- to print
;with cte_sort as
(
select md.POS_ID,
min(md.uniqueid) as sort_num
from dbo.input_Main_data md
group by md.POS_ID
)
select md.POS_ID,
md.Term_Code,
md.Status,
md.IsActive
from dbo.input_Main_data md
join cte_sort s
on s.POS_ID = md.POS_ID
order by s.sort_num,
md.Term_Code,
md.IsActive desc;
I have a table tblTags (Date, Tagindex, Value)
The values in the table are:
Date Tagindex Value
---------------------------------
2017-10-21 0 21
2017-10-21 1 212
2017-10-21 2 23
2017-10-21 0 34
2017-10-21 1 52
2017-10-21 2 65
I want the result as :
Date 0 1 2
-------------------------------
2017-10-21 21 212 23
2017-10-21 34 52 65
For this I wrote the followring query
select *
from
(SELECT a.Date, a.Tagindex,a.value
FROM tblTag a) as p
pivot
(max(value)
for Tagindex in ( [tblTag])
) as pvt
But I get these errors:
Msg 8114, Level 16, State 1, Line 10
Error converting data type nvarchar to int.
Msg 473, Level 16, State 1, Line 10
The incorrect value "tblTag" is supplied in the PIVOT operator.
How to solve this issue.
I think can use a query like this:
;with t as (
select *
, row_number() over (partition by [Date],[Tagindex] order by (select 0)) seq
from tblTag
)
select [Date],
max(case when [Tagindex] = 0 then [Value] end) '0',
max(case when [Tagindex] = 1 then [Value] end) '1',
max(case when [Tagindex] = 2 then [Value] end) '2'
from t
group by [Date], seq;
SQL Server Fiddle Demo
SQL Server Fiddle Demo - with pivot
Note: In above query I use row_number() function to create a sequence number for each Date and Tagindex, But the trick is in using (select 0) that is a temporary field to use in order by part, that will not trusted to return arbitrary order of inserted rows.So, if you need to achieve a trusted result set; you need to have an extra field like a datetime or an auto increment field.
Try this:
DECLARE #tblTag TABLE
(
[Date] DATE
,[TagIndex] TINYINT
,[Value] INT
);
INSERT INTO #tblTag ([Date], [TagIndex], [Value])
VALUES ('2017-10-21', 0, 21)
,('2017-10-21', 1, 212)
,('2017-10-21', 2, 23)
,('2017-10-22', 0, 34)
,('2017-10-22', 1, 52)
,('2017-10-22', 2, 65);
SELECT *
FROM #tblTag
PIVOT
(
MAX([value]) FOR [Tagindex] IN ([0], [1], [2])
) PVT;
You need to say exactly which are the PIVOT columns. If you are going to have different values for the TagIndex and you cannot hard-coded them, you need to use dynamic PIVOT.
Also, you need to be sure you have a way to group the tagIndex values in one row. For example, different date (as in my test data), ID column which is marking when a row is inserted or something else (group ID column or date added column).
I have an existing database where some logic is made by the front end application.
Now I have to make reports from that database and I'm facing to a proble of missing records which are covered on a record basis in the frontend but have issues in the report
Given the following tables:
create table #T (id int, id1 int, label varchar(50))
create table #T1 (id int, T_id1 int, A int, B int, C int)
With the values:
insert into #T values (10, 1, 'label1'), (10, 2, 'label2'), (10, 3, 'label3'), (10, 15, 'label15'), (10, 16, 'label16'), (20, 100, 'label100'), (20, 101, 'label101')
insert into #T1 values (10, 1, 100, 200, 300), (10, 15, 150, 250, 350), (20, 100, 151, 251, 351), (20, 101, 151, 251, 351)
if I make a report we can see some missing records:
select #T.id, #T.id1, #T1.A, #T1.B, #T1.C
from #T left join #T1 on #T.id1 = #T1.T_id1
result:
id id1 A B C
10 1 100 200 300
10 2 NULL NULL NULL
10 3 NULL NULL NULL
10 15 150 250 350
10 16 NULL NULL NULL
20 100 151 251 351
20 101 151 251 351
Expected result would be:
id id1 A B B
10 1 100 200 300
10 2 100 200 300
10 3 100 200 300
10 15 150 250 350
10 16 150 250 350
20 100 151 251 351
20 101 151 251 351
As you can see here the missing data is filled out of the the first (in id, id1 order) previous existing record for a given id. For a given id there can be any number of "missing" records and for the given id there can be any number of existing records after a not existing ones.
I can do this with a cursor but I'm looking for a solution without cursor
You can use subquery (to find groups with same values) + window function
WITH Grouped AS (
SELECT #T.id, #T.id1, #T1.A, #T1.B, #T1.C,
GroupN = SUM(CASE WHEN #T1.A IS NULL THEN 0 ELSE 1 END) OVER(/* PARTITION BY id ? */ ORDER BY id1 ROWS UNBOUNDED PRECEDING)
FROM #T
LEFT JOIN #T1 ON #T.id1 = #T1.T_id1
)
SELECT Grouped.id, Grouped.id1,
A = MAX(A) OVER(PARTITION BY GroupN),
B = MAX(B) OVER(PARTITION BY GroupN),
C = MAX(C) OVER(PARTITION BY GroupN)
FROM Grouped
You can use below sql for thedesired output:
with cte (id, id1, A, B, C)
as
(
select #T.id, #T.id1, #T1.A, #T1.B, #T1.C
from #T left join #T1 on #T.id1 = #T1.T_id1
)
select cte.id, cte.id1,
coalesce(cte.A,TT.A) A,
coalesce(cte.B,TT.B) B,
coalesce(cte.C,TT.C) C
from cte
left join
(
select p.id1,max(q.id1) id1_max
from cte p
inner join cte q on p.id1 > q.id1 and p.a is null and q.a is not null
group by p.id1
) T on cte.id1 = T.id1
left join cte TT on T.id1_max = TT.id1
Title sounds confusing but let me please explain:
I have a table that has two columns that provide a date range, and one column that provides a value. I need to query that table and "detail" the data such as this
Is it possible to do only using TSQL?
Additional Info
The table in question is about 2-3million records long (and growing)
Assuming the range of dates is fairly narrow, an alternative is to use a recursive CTE to create a list of all dates in the range and then join interpolate to it:
WITH LastDay AS
(
SELECT MAX(Date_To) AS MaxDate
FROM MyTable
),
Days AS
(
SELECT MIN(Date_From) AS TheDate
FROM MyTable
UNION ALL
SELECT DATEADD(d, 1, TheDate) AS TheDate
FROM Days CROSS JOIN LastDay
WHERE TheDate <= LastDay.MaxDate
)
SELECT mt.Item_ID, mt.Cost_Of_Item, d.TheDate
FROM MyTable mt
INNER JOIN Days d
ON d.TheDate BETWEEN mt.Date_From AND mt.Date_To;
I've also assumed an that date from and date to represent an inclusive range (i.e. includes both edges) - it is unusual to use inclusive BETWEEN on dates.
SqlFiddle here
Edit
The default MAXRECURSION on a recursive CTE in Sql Server is 100, which will limit the date range in the query to a span of 100 days. You can adjust this to a maximum of 32767.
Also, if you are filtering just a smaller range of dates in your large table, you can adjust the CTE to limit the number of days in the range:
WITH DateRange AS
(
SELECT CAST('2014-01-01' AS DATE) AS MinDate,
CAST('2014-02-16' AS DATE) AS MaxDate
),
Days AS
(
SELECT MinDate AS TheDate
FROM DateRange
UNION ALL
SELECT DATEADD(d, 1, TheDate) AS TheDate
FROM Days CROSS APPLY DateRange
WHERE TheDate <= DateRange.MaxDate
)
SELECT mt.Item_ID, mt.Cost_Of_Item, d.TheDate
FROM MyTable mt
INNER JOIN Days d
ON d.TheDate BETWEEN mt.Date_From AND mt.Date_To
OPTION (MAXRECURSION 0);
Update Fiddle
This can be achieved using Cursors.
I've simulated the test data provided and created another table with the name "DesiredTable" to store the data inside, and created the following cusror which achieved exactly what you are looking for:
SET NOCOUNT ON;
DECLARE #ITEM_ID int, #COST_OF_ITEM Money,
#DATE_FROM date, #DATE_TO date;
DECLARE #DateDiff INT; -- holds number of days between from & to columns
DECLARE #counter INT = 0; -- for loop counter
PRINT '-------- Begin the Date Expanding Cursor --------';
-- defining the cursor target statement
DECLARE Date_Expanding_Cursor CURSOR FOR
SELECT [ITEM_ID]
,[COST_OF_ITEM]
,[DATE_FROM]
,[DATE_TO]
FROM [dbo].[OriginalTable]
-- openning the cursor
OPEN Date_Expanding_Cursor
-- fetching next row data into the declared variables
FETCH NEXT FROM Date_Expanding_Cursor
INTO #ITEM_ID, #COST_OF_ITEM, #DATE_FROM, #DATE_TO
-- if next row is found
WHILE ##FETCH_STATUS = 0
BEGIN
-- calculate the number of days in between the date columns
SELECT #DateDiff = DATEDIFF(day,#DATE_FROM,#DATE_TO)
-- reset the counter to 0 for the next loop
set #counter = 0;
WHILE #counter <= #DateDiff
BEGIN
-- inserting rows inside the new table
insert into DesiredTable
Values (#COST_OF_ITEM, DATEADD(day,#counter,#DATE_FROM))
set #counter = #counter +1
END
-- fetching next row
FETCH NEXT FROM Date_Expanding_Cursor
INTO #ITEM_ID, #COST_OF_ITEM, #DATE_FROM, #DATE_TO
END
-- cleanup code
CLOSE Date_Expanding_Cursor;
DEALLOCATE Date_Expanding_Cursor;
The code fetches every row from your original table, then it calculates the number of days between DATE_FROM and DATE_TO columns, then using this number the script will create identical rows to be inserted inside the new table DesiredTable.
give it a try and let me know of the results.
You can generate an increment table and join it to your date From:
Query:
With inc(n) as (
Select ROW_NUMBER() over (order by (select 1)) -1 From (
Select 1 From (values(1), (1), (1), (1), (1), (1), (1), (1), (1), (1)) as x1(n)
Cross Join (values(1), (1), (1), (1), (1), (1), (1), (1), (1), (1)) as x2(n)
) as x(n)
)
Select item_id, cost, DATEADD(day, n, dateFrom), n From #dates d
Inner Join inc i on n <= DATEDIFF(day, dateFrom, dateTo)
Order by item_id
Output:
item_id cost Date n
1 100 2014-01-01 00:00:00.000 0
1 100 2014-01-02 00:00:00.000 1
1 100 2014-01-03 00:00:00.000 2
2 105 2014-01-08 00:00:00.000 2
2 105 2014-01-07 00:00:00.000 1
2 105 2014-01-06 00:00:00.000 0
2 105 2014-01-09 00:00:00.000 3
3 102 2014-02-14 00:00:00.000 3
3 102 2014-02-15 00:00:00.000 4
3 102 2014-02-16 00:00:00.000 5
3 102 2014-02-11 00:00:00.000 0
3 102 2014-02-12 00:00:00.000 1
3 102 2014-02-13 00:00:00.000 2
Sample Data:
declare #dates table(item_id int, cost int, dateFrom datetime, dateTo datetime);
insert into #dates(item_id, cost, dateFrom, dateTo) values
(1, 100, '20140101', '20140103')
, (2, 105, '20140106', '20140109')
, (3, 102, '20140211', '20140216');
Yet another way is to create and maintain calendar table, containing all dates for many years (in our app we have table for 30 years or so, extending every year). Then you can just link to calendar:
select <whatever you need>, calendar.day
from <your tables> inner join calendar on calendar.day between <min date> and <max date>
This approach allows to include additional information (holidays etc) in calendar table - sometimes very helpful.
SQL Server 2000
My Table:
CARDNO CARDEVENTDATE CARDEVENTTIME
121 20090610 025050
121 20090611 040000
121 20090611 050000
121 20090611 020000
122 20090611 030001
122 20090611 030000
123 20090611 080000
123 20090611 100000
123 20090611 132449
123 20090611 025959
124 20090610 030000
124 20090612 030001
125 20090611 030002
125 20090612 040000
Cardno is Separate Table
Cardeventdate, cardeventtime is separate table
From the above table I want to get Top Time and Bottom Time for the Particular cardeventdate and Cardno
For the 121, 20090611, Top Time is 040000, Bottom Time is 020000
For 123, 20090611, Top Time is 080000, Bottom Time is 025959 …
Like this I need.
I used Min (time) and Max (time), But it showing like this.
For CardNo – 121
Cardeventdate – 20090611
Min Time – 020000
Max Time – 040000
I don’t want to get min and Max, I need only top and Bottom (or) First and Last time value of the particular Date and Cardno.
I used this Query
SELECT RowNumber = IDENTITY (int, 1, 1), CARDNO, CARDEVENTDATE, CARDEVENTTIME INTO #Table1 FROM T_CARDEVENT SELECT t1.CARDNO, t1.CARDEVENTDATE, t1.CARDEVENTTIME FROM #Table t1 INNER JOIN (SELECT RowNumber = MIN(RowNumber), CARDEVENTDATE, CARDNO FROM #Table1 t WHERE (cardeventdate > 20090601) GROUP BY cardno, cardeventdate UNION ALL SELECT MAX(RowNumber), CARDEVENTDATE, CARDNO FROM #Table1 t WHERE (cardeventdate > 20090601) GROUP BY cardno, cardeventdate) t2 ON t2.rownumber = t1.rownumber
Output:
ROWNUMBER CARDNO CARDEVENTDATE CARDEVENTTIME
335 0121 20090611 040000
1099 0121 20090611 050000
1100 0121 20090611 025050
336 0121 20090612 020000
337 0122 20090611 030001
338 0122 20090612 030000
339 0123 20090611 080000
1101 0123 20090611 100000
1102 0123 20090611 132449
340 0123 20090612 025959
341 0124 20090611 030000
342 0124 20090612 030001
343 0125 20090611 030002
344 0125 20090612 040000
So Here Row Number is created for all columns, from that how I have to take First Time and Last Time for the Particular Date.
Expecting Output
CARDNO CARDEVENTDATE CARDEVENTTIME Expecting
0121 20090611 040000 Top Value
0121 20090611 020000 No Need
0121 20090611 025050 Bottom Value
……… so on
Need Query Help.
Well, unless you have some additional fields to establish an order, this is non deterministic. Given the three values for the 23-04-2009 - how is it that APPLE is the first and ROSE is the last? If the ID and the DATE are the same for all three entries, there's no order defined to filter out "GRAPHE"......
Marc
UPDATE: I expanded on Lieven's idea a bit and got this working in my setup:
DECLARE #TempTable TABLE (RowNumber INT IDENTITY(1,1),
DayNumber INT,
ID VARCHAR(3), DateField DATETIME, Value VARCHAR(32))
INSERT INTO #TempTable(DayNumber, id, datefield, value)
SELECT DATEPART(DAYOFYEAR, DateField), ID, DateField, Value
FROM #Table
SELECT *
FROM #TempTable t
INNER JOIN
(SELECT RowNumber = MIN(RowNumber), DayNumber, ID
FROM #TempTable t
GROUP BY DayNumber, t.ID
UNION ALL
SELECT MAX(RowNumber), DayNumber, ID
FROM #TempTable t
GROUP BY DayNumber, t.ID) t2
ON t2.RowNumber = t.RowNumber
GO
I'm basically creating a temp table with additional info - an artificial "RowNumber" to create some order, and the "DayNumber" to get dates grouped by date only (without time).
Seems to work ok for me - does it work for you, too?
Jash, if you execute this script, does it give you the results you'd expect?
CREATE TABLE #T_Cardevent (CARDNO VARCHAR(3), CARDEVENTDATE VARCHAR(8), CARDEVENTTIME VARCHAR(8))
INSERT INTO #T_Cardevent VALUES ('121', '20090610', '025050')
INSERT INTO #T_Cardevent VALUES ('121', '20090611', '040000')
INSERT INTO #T_Cardevent VALUES ('121', '20090611', '050000')
INSERT INTO #T_Cardevent VALUES ('121', '20090611', '020000')
INSERT INTO #T_Cardevent VALUES ('122', '20090611', '030001')
INSERT INTO #T_Cardevent VALUES ('122', '20090611', '030000')
INSERT INTO #T_Cardevent VALUES ('123', '20090611', '080000')
INSERT INTO #T_Cardevent VALUES ('123', '20090611', '100000')
INSERT INTO #T_Cardevent VALUES ('123', '20090611', '132449')
INSERT INTO #T_Cardevent VALUES ('123', '20090611', '025959')
INSERT INTO #T_Cardevent VALUES ('124', '20090610', '030000')
INSERT INTO #T_Cardevent VALUES ('124', '20090612', '030001')
INSERT INTO #T_Cardevent VALUES ('125', '20090611', '030002')
INSERT INTO #T_Cardevent VALUES ('125', '20090612', '040000')
SELECT
RowNumber = IDENTITY (int, 1, 1)
, CARDNO
, CARDEVENTDATE
, CARDEVENTTIME
INTO #Table
FROM #T_CARDEVENT
SELECT t1.CARDNO, t1.CARDEVENTDATE, t1.CARDEVENTTIME
FROM #Table t1
INNER JOIN (
SELECT RowNumber = MIN(RowNumber), CARDEVENTDATE, CARDNO
FROM #Table t
GROUP BY cardno, cardeventdate
UNION ALL SELECT MAX(RowNumber), CARDEVENTDATE, CARDNO
FROM #Table t
GROUP BY cardno, cardeventdate) t2 ON t2.rownumber = t1.rownumber
ORDER BY 1, 2, 3
DROP TABLE #Table
DROP TABLE #T_Cardevent
I havn't tried it, but maybe something like this might do it:
SELECT TOP(1) FROM Table
WHERE Date='Some-date'
AND Id=Some-Id
ORDER BY Date ASC
UNION
SELECT TOP(1) FROM Table
WHERE Date='Some-date'
AND Id=Some-Id
ORDER BY Date Desc
to cheat you can add an autonumber feild
AUtoID ID DATE VALUE
1 001 23:04:2009 APPLE
2 001 23:04:2009 GRAPHE
3 001 23:04:2009 ROSE
4 001 24:04:2009 BERRY
5 001 24:04:2009 TIFFANY
6 001 24:04:2009 ORGANE
7 001 24:04:2009 SILVER
You can then do min and max against it
I usually use insert into a tempory table which is defined with the ID, technically the insertion order isn't garenteed going in.
Here's the solution using inline views:
with
tempFirst as (
select id, date, value,
row_number() over (partition by id, date order by date asc) as rownum1
from table1
),
tempLast as (
select *,
row_number() over (partition by id, date order by rownum1 desc) as rownum2
from tempFirst
)
select id, date, value from tempFirst where rownum1 = 1
union
select id, date, value from tempLast where rownum2 = 1
I have tested the output, which is:
ID DATE VALUE
001 2009-04-23 APPLE
001 2009-04-23 ROSE
001 2009-04-24 BERRY
001 2009-04-24 SILVER