Please start any new threads on our new
site at https://forums.sqlteam.com. We've got lots of great SQL Server
experts to answer whatever question you can come up with.
Author |
Topic |
DOlivastro
Starting Member
41 Posts |
Posted - 2006-01-19 : 12:29:11
|
MSAccess has two nice aggregate functions called FIRST and LAST. If you don't know how they work, FIRST simply returns the first record in the aggregate, LAST returns the last record.It seems that these functions do not appear in SQL_Server. Is there any way to code SQL so that we get the same functionality?BTW, is there any reason why MS does not incorporate these functions.Dom |
|
Arnold Fribble
Yak-finder General
1961 Posts |
Posted - 2006-01-19 : 12:59:18
|
quote: Originally posted by DOlivastro FIRST simply returns the first record in the aggregate, LAST returns the last record.
First and last record according to what?The Access 95 through Access 2000 help files say: "Since records are normally returned in no particular order (unless the query includes an ORDER BY clause), the records returned by these functions will be arbitrary."The Access 2002 help says, "These functions return the first or last record as it was entered in chronological order. Sorting the records has no effect on these functions." |
 |
|
jhermiz
3564 Posts |
Posted - 2006-01-19 : 16:51:24
|
What about good ol Min() and Max() ? Keeping the web experience alive -- [url]http://www.web-impulse.com[/url]RS Blog -- [url]http://weblogs.sqlteam.com/jhermiz[/url] |
 |
|
DOlivastro
Starting Member
41 Posts |
Posted - 2006-01-19 : 21:05:42
|
To Arnold Fibble: Thanks, I didn't know that MS had changed the definition of FIRST and LAST.To jhermiz: It is not the same a Min and Max. I want a function that returns the first record after the records have been ordered. Also, this is not the same TOP 1, since I don't want just the first record, I want the first record of every group composed by a GROUP BY clause and ordered by a ORDER BY clause.For example, lets say a group is resturned as follows (GROUP BY Name, ORDERED BY age):Name Age ScoreDom 13 70Dom 15 65Dom 20 90Then Min (Score) will return 65, but First (Score) will return 70. |
 |
|
madhivanan
Premature Yak Congratulator
22864 Posts |
Posted - 2006-01-20 : 01:58:21
|
This may help youDeclare @t table(Name varchar(20),Age int,Score int)Insert into @t select 'Dom', 13, 70 union all select 'Dom', 15, 65 union all select 'Dom', 20, 90 union all select 'Dim', 33, 12 union all select 'Dim', 25, 56 union all select 'Dim', 67, 78 select * from @t Twhere score=(select top 1 score from @t where name=T.name) MadhivananFailing to plan is Planning to fail |
 |
|
druer
Constraint Violating Yak Guru
314 Posts |
Posted - 2006-01-20 : 16:28:18
|
Select TOP 1 ..... order by ASC would give you just the FIRST oneSelect TOP 1 ..... order by DESC would give you the LAST one |
 |
|
notspecified
Starting Member
2 Posts |
Posted - 2006-02-02 : 14:44:55
|
Here is the real problem.Here is sample data from table fakeTable ordered by Date DESC.ID Date col1 col2 col3 col41 2005-01-03 NULL 27 32 NULL1 2005-01-02 NULL 29 NULL 441 2005-01-01 99 13 5 12I want to write a query like this.SELECT ID, FIRST(col1), FIRST(col2), FIRST(col3), FIRST(col4)FROM fakeTableGROUP BY IDORDER BY Date DESCThis is the result that I want.ID col1 col2 col3 col41 99 27 32 44Of course I can't do this because 1) there is no aggregate function FIRST, 2) I can't specify Date in the ORDER BY clause because it is not included in the GROUP BY clause and 3) MS SQL aggregate functions do not utilize ORDER when calculating.I've solved the problem with a stored procedure but, even though I didn't use cursors in my stored procedure, it takes unacceptable long to process.It would be useful if there was a built in aggregate function for this. |
 |
|
notspecified
Starting Member
2 Posts |
Posted - 2006-02-02 : 17:11:24
|
Here is a possible solution.SELECT ID, CAST (CASE WHEN LEN(MAX(CONVERT(varchar(50),Date,126) + '|' + CAST(col1 AS varchar(50)))) > 0THEN RIGHT(MAX(CONVERT(varchar(50),Date,126) + '|' + CAST(col1 AS varchar(50))), LEN(MAX(CONVERT(varchar(50),Date,126) + '|' + CAST(col1 AS varchar(50)))) - CHARINDEX('|',MAX(CONVERT(varchar(50),Date,126) + '|' + CAST(col1 AS varchar(50)))))ELSE NULLENDAS Float) AS col1FROM fakeTableGROUP BY IDYou can apply the same function to col2, col3 and col4 in the same query and you will get the FIRST non-null value for each col as if ordered by Date DESC. |
 |
|
rockmoose
SQL Natt Alfen
3279 Posts |
Posted - 2006-02-03 : 19:04:54
|
quote: Originally posted by DOlivastro For example, lets say a group is resturned as follows (GROUP BY Name, ORDERED BY age):Name Age ScoreDom 13 70Dom 15 65Dom 20 90Then Min (Score) will return 65, but First (Score) will return 70.
This is where you make your biggest mistake, there is no "first" or "last".The tables:Name Age ScoreDom 13 70Dom 15 65Dom 20 90 Name Age ScoreDom 15 65Dom 13 70Dom 20 90 Are identical from a relational database perspective.There is no notion of "first" and "last", you have to order by something, to make sense of your nonsense.rockmoose |
 |
|
irashkin
Starting Member
1 Post |
Posted - 2007-07-09 : 07:53:35
|
Perhaps FIRST/LAST is a misnomer, but I am among those who would love a function like this on a regular basis. Why? Sometimes I need to create new rows based on old rows - say, a summary row, or something similar. For instance, suppose I have a user table, with name, company, and company address. I want to get a list of companies, and I would like to include an address. Now, there may be multiple addresses, as users have been entered either for different branches, or at different times with teh company address changing. So for starters I can do select distinct companyname from thistablebut I don't get the address. I could do select companyname, min(address), min(city), [etc.] group by companynamebut a quick sanity check tells me that I may end up with mismatched address/city/etc. combinations. See, I don't really care which address I use - I just want something in there that at least MIGHT be valid - but I do want a legit address, not something like 123 Main St., San Francisco, AZ. San Fancisco just is not in Arizna (ok, maybe there is one there by that name, but you get my meaning).So I don't care if it is FIRST or LAST, but I want a deterministic row selector - each column that I use that aggregate for should come from the same row.Of course, I can (and have) written my own functions for this, or just built the logic into my procedures as needed, but it really would be handy to have a built in function, and mainly, I just wanted to point out the validity (n my mind at least) of such a request. |
 |
|
X002548
Not Just a Number
15586 Posts |
Posted - 2007-07-09 : 11:04:43
|
The physical order of data in a database has no meaning, and since Access2002 has clarified what it means, they have internally performed a hack to now which row was inserted before anotherYou need an identity column in the database or a datetime column that defaults to getdate() on insertBrett8-)Hint: Want your questions answered fast? Follow the direction in this linkhttp://weblogs.sqlteam.com/brettk/archive/2005/05/25/5276.aspxAdd yourself!http://www.frappr.com/sqlteam |
 |
|
LoztInSpace
Aged Yak Warrior
940 Posts |
Posted - 2007-07-09 : 21:48:06
|
quote: Originally posted by irashkin See, I don't really care which address I use
I call bullshit. Why would any old address be of any more use to anyone than any other? It sounds to me like the address may not as well be there if it doesn't matter what it is. Seems like a dodgy requirement/business case to me.Just do it properly - do a table join with a min(), max() or some other deterministic function in the predicate. Fix your data model if you have to - it certainaly seems broken if you have the "same" company in a table more than once. |
 |
|
KevinKembel
Starting Member
11 Posts |
Posted - 2008-03-17 : 13:34:48
|
I realize this post is a little dated, but I have a similar problem, and thought someone might be able to offer a solution. first() and last() would be perfect, but if requiring those functions means there's a problem in my data model, I'd like to find out how to fix that too :)So, for simplicity's sake, let's say I have a products table.ProductID,ProductDescriptionAnd a productPrice table that will track pricing history, every product will have a ProductPrice that has IsMSRP = 'True', but custom prices can be set where IsMSRP = 'False'ProductID,ProductPrice,PriceDate,IsMSRPIf I do a select * from products inner join productprice on products.ProductID = productprice.ProductIDorder by (isMSRP ASC, PriceDate DESC)the first records will be custom prices, followed by the most recent MSRP prices. So how can I do a select products.*, () AS [CustomPrice], () AS [MSRP]using an inner join, and not using any nested selects (I know nested selects would be easy, but I wise-man once told me that pretty much any nested select can be done using joins, and I also can't do any indexed views using nested selects which is what I'm looking for) |
 |
|
jimf
Master Smack Fu Yak Hacker
2875 Posts |
|
X002548
Not Just a Number
15586 Posts |
|
Rahm
Starting Member
1 Post |
Posted - 2008-03-19 : 06:48:14
|
Hi, I am new to T-SQL and come from MS Access background.Like Kevin, I am used to a GROUP BY query with First and Last of one of the fields, picked from the sorted table/recordset below.Question:How do I get the latest price for each product in the list of product & prices in the following table.DDL:CREATE TABLE [dbo].[myTABLE]( [PRODUCT] [nchar](10), [PRICE] [decimal](18, 4), [DATE_PRICED] [datetime] ) DATA USED in TABLEPRODUCT PRICE DATE_PRICEDRT10-111/VY, 1, 2007-03-16RT10-112/VY, 0.95, 2007-06-01RT10-112/VY, 0.95, 2006-05-16RT10-112/VY, 0.92, 2002-09-23RT10-115, 3.2, 2007-04-03RT10-116, 2.5, 2007-12-07RT10-120/VY, 1.2, 2006-03-17RT10-120/VY, 1.15, 2004-04-12RT10-201/VY, 4.06, 2004-11-29RT10-211, 0.59, 2007-09-12RT10-212/BD, 0.85, 2007-01-10RT10-213, 31.5, 2007-11-01RT10-327/MA, 1.78, 2001-06-29RT10-328, 1.26, 2007-09-27RT10-420/FL, 0.3991, 2007-11-05RT10-421/UN, 0, 2006-04-04RT10-421/UN, 60, 2004-09-13RT10-422/PE, 0.45, 2006-07-21RT10-422/PE, 0.504, 2003-03-03RT10-427/FL, 0.3991, 2008-01-04RT10-427/FL, 0.4016, 2007-12-17RT10-430/FL, 0.4339, 2004-06-14RT10-430/FL, 0.42, 2004-04-05RT10-435/FL, 0.49, 2004-05-03THE QUERYI would use the following MS Access query but cannot find the equivalent T-SQL quer/ies to do this:SELECT PRODUCT, First(DATE_PRICED) AS LASTDATE, First(PRICE) AS LASTPRICEFROM MYTABLEGROUP BY PRODUCTPlease note I have already run a query to sort the above table in descending date for each product.RESULTSET:FOR EACH SINGLE PRODUCT, THE RESULTSET WOULD THEN HAVE BEEN:CODE LASTPRICE LASTDATERT10-111/VY 1 2007-03-16RT10-112/VY 0.95 2007-06-01RT10-115 3.2 2007-04-03RT10-116 2.5 2007-12-07RT10-120/VY 1.2 2006-03-17RT10-201/VY 4.06 2004-11-29RT10-211 0.59 2007-09-12RT10-212/BD 0.85 2007-01-10RT10-213 31.5 2007-11-01RT10-327/MA 1.78 2001-06-29RT10-328 1.26 2007-09-27RT10-420/FL 0.3991 2007-11-05RT10-421/UN 0 2006-04-04RT10-422/PE 0.45 2006-07-21RT10-427/FL 0.3991 2008-01-04RT10-430/FL 0.4339 2004-06-14RT10-435/FL 0.49 2004-05-03Is there anyone who has carried out similar queries with MS Access and migrated them to T-SQL?Alternatively, anyone who does similar query in T-SQL itself?Many thanks for your assistance and patience.Rahm M |
 |
|
visakh16
Very Important crosS Applying yaK Herder
52326 Posts |
Posted - 2008-03-19 : 07:20:45
|
SELECT t1.PRODUCT, t1.DATE_PRICED AS LASTDATE, t1.PRICE AS LASTPRICEFROM MYTABLE t1INNER JOIN (SELECT PRODUCT, MAX(DATE_PRICED) AS LATESTDATEFROM MYTABLE GROUP BY PRODUCT)t2ON t1.PRODUCT=t2.PRODUCTAND t1.DATE_PRICED=t2.LATESTDATE |
 |
|
CZahrobsky
Starting Member
1 Post |
Posted - 2011-05-05 : 12:50:11
|
It's not pretty, but I combine fields to sort alpha with my target field in the last position, get the MAX or MIN of that, and return the substring of just the target field: SELECT [PRODUCT], CONVERT(decimal, SUBSTRING( MIN(convert(varchar, [DATE_PRICED], 126) + ' ' + convert(varchar, [PRICE]) ), 25, 50) AS FIRSTPRICEGROUP BY [PRODUCT] Notes: The MIN(convert(varchar, [DATE_PRICED], 126) allows the date part to sort alphabetically in this format 2011-05-05T11:45:55.750, hence the SUBSTRING 25 characters in. The 50 is arbitrary, as long as it encompasses your desired field length.The tree of knowledge has many tangled roots. |
 |
|
sunnysood
Starting Member
2 Posts |
Posted - 2012-12-17 : 07:11:18
|
quote: Originally posted by DOlivastro MSAccess has two nice aggregate functions called FIRST and LAST. If you don't know how they work, FIRST simply returns the first record in the aggregate, LAST returns the last record.It seems that these functions do not appear in SQL_Server. Is there any way to code SQL so that we get the same functionality?BTW, is there any reason why MS does not incorporate these functions.Dom
LOL |
 |
|
sunnysood
Starting Member
2 Posts |
Posted - 2012-12-17 : 07:16:32
|
quote: Originally posted by DOlivastro MSAccess has two nice aggregate functions called FIRST and LAST. If you don't know how they work, FIRST simply returns the first record in the aggregate, LAST returns the last record.It seems that these functions do not appear in SQL_Server. Is there any way to code SQL so that we get the same functionality?BTW, is there any reason why MS does not incorporate these functions.Dom
I had the same problem today. the first and last functions do not appear to work in sql server but there are ways around it using the top clause.Get the first entry:select top 1 column_name as first_entryfrom table_nameGet the last entry:select top 1 Order_Price as Last_Entryfrom table_name order by column_name desccheersLOL |
 |
|
KipB7
Starting Member
1 Post |
Posted - 2013-04-17 : 19:15:21
|
For Microsoft SQL Server, CROSS APPLY can save the day in cases like this. Here's an example where you want to know the scores on the contest by Age, with high/low/average/count for each age group.IF OBJECT_ID('T1','U') IS NULL --drop table T1 CREATE TABLE T1 (Name varchar(20), Age int, Score DECIMAL(9,0))IF NOT EXISTS(SELECT * FROM T1) Insert into T1---Age-Score-- select 'Dam', 13, 70 UNION ALL select 'Dem', 13, 65 UNION ALL select 'Dim', 25, 12 UNION ALL select 'Dom', 25, 56 UNION ALL select 'Dum', 67, 78 UNION ALL SELECT 'Duz', 25, 57SELECT Age, HI.NAME[HiGuy],HI.Score[HiScore],LO.NAME[LoGuy],LO.Score[LoScore], AvgScore, HowMany[#]FROM (SELECT DISTINCT Age FROM T1) t0CROSS APPLY (SELECT TOP 1 Name, Score FROM T1 WHERE T1.Age=t0.Age ORDER BY Score DESC) HICROSS APPLY (SELECT TOP 1 Name, Score FROM T1 WHERE T1.Age=t0.Age ORDER BY Score ASC) LOCROSS APPLY ( SELECT COUNT(*)[HowMany], CONVERT(DECIMAL(9,2),AVG(score))[AvgScore] FROM T1 WHERE T1.Age=t0.Age ) AVGSCORDER BY Age/*Age HiGuy HiSc LoGuy LoSc AvgSc #13 Dam 70 Dem 65 67.50 225 Duz 57 Dim 12 41.67 367 Dum 78 Dum 78 78.00 1*/ |
 |
|
Next Page
|
|
|
|
|