SQL -- Selecting Top 1 with Order by? - sql

This was resolved. The statement was in another part of the stored procedure.
The stored procedure I'm writing won't allow me to do this:
declare #dtTopDate datetime
select top 1 #dtTopDate = date_build
from database..table
where database..table.parent = #Parent
and database..table.child = #Child
order by date_build desc
Gives me this error:
Column "database..table.date_build" is
invalid in the ORDER BY clause because
it is not contained in either an
aggregate function or the GROUP BY
clause.
What am I doing wrong?
[Edit] There is no group by statement here. SQL2005.
Here is some more context:
if #Notify = 0
begin
declare #dtTopDate datetime
select top 1 #dtTopDate = date_build
from database..table
where database..table.parent = #Parent
and database..table.child = #Child
order by date_build desc
insert
into database2..table
(parent, child, notification_date, change_date)
values (#Parent, #Child, #dtTopDate, getdate())
return
end

This works for me, but I'm not sure if this is what you are trying to do b/c your example has some errors.
use Test
go
CREATE TABLE [dbo].[MyTable]
(
[MyTableId] [uniqueidentifier] NOT NULL,
[MyDate] [datetime] NOT NULL,
CONSTRAINT [PK_MyTable] PRIMARY KEY CLUSTERED([MyTableId] ASC,[MyDate] ASC)
)
GO
CREATE PROCEDURE ProcTopDate
(
#MyDate datetime OUT
)
AS
BEGIN
SET NOCOUNT ON;
SELECT TOP 1
#MyDate = [MyDate]
FROM [Test].[dbo].[MyTable]
order by MyDate
END
GO
insert into MyTable(MyTableId, MyDate)
values(newid(), getdate())
go
declare #MyDate datetime
exec ProcTopDate #MyDate OUT
print #MyDate

Instead of SELECT TOP 1 ... ORDER BY ...
Why not try SELECT MAX( ..
DECLARE #dtTopDate datetime
SELECT #dtTopDate = MAX(date_build)
from database..table
where database..table.parent = #Parent
and database..table.child = #Child

What version of SQL are you using? It works fine for me on MS SQL Server 2005 (ionce I fix the declaration).

Honestly the only thing I can see wrong is that #dtTopDate =/= #dtLatestDate
Apart from that, there is no GROUP BY clause in your SQL statement.
I just ran this and it worked fine.
declare #OrderDate datetime
select top 1 #OrderDate = OrderDate
from Orders
where Orders.CustomerID = 'ALFKI'
and Orders.EmployeeID = 4
order by OrderDate desc
SELECT #OrderDate

Try qualifying the columns correctly to avoid any ambiguities or x-database schema issue
declare #dtTopDate datetime
select top 1
#dtTopDate = [database]..[table].date_build
FROM
[database]..[table]
where
[database]..[table].parent = #Parent
and [database]..[table].child = #Child
order by
[database]..[table].date_build desc
Or alias it
declare #dtTopDate datetime
select top 1
#dtTopDate = foo.date_build
FROM
[database]..[table] foo
where
foo.parent = #Parent
and foo.child = #Child
order by
foo.date_build desc

The problem was in another part of the stored procedure. I was using a count(*) elsewhere and it required a group by. Thanks for the help.

Try SELECT #dtLatestDate = TOP 1 date_build...

if you want to get really tricky, in T-SQL you can try using the row_number() method and an inner select:
select * from
(
select
db.groupId
, db.date_build
, date_build_rank = row_number() over ( partition by db.groupId order by db.date_build desc)
from
#date_build_tbl db
) as a
where a.date_build_rank < 2;

Related

SQL Server order by a function value

In the order by clause, can we specify a function? It works for inbuilt function like ORDER BY NEWID() since NEWID() is a function. But there I want to specify ordering by a custom function.
ALTER Function [dbo].[Func_RetentionPriorityStatus]
(
#Priority varchar(3)
)
Returns varchar(50)
As
Begin
declare #ID int
set #ID = (select rop.ID from dbo.RetentionOutboundPriority rop where rop.Archived = 0);
declare #ColName varchar(50)
select
#ColName = Col.value('local-name(.)', 'varchar(max)')
from (select *
from dbo.RetentionOutboundPriority
where ID = #ID
for xml path(''), type) as T(XMLCol)
cross apply
T.XMLCol.nodes('*') as n(Col)
where Col.value('.', 'varchar(10)') = #Priority
RETURN #ColName
End
So the query is like this
select * from dbo.RetentionTele rt
inner join dbo.RetentionTeleNotInterested rtni
on rt.ID = rtni.RetentionTeleID
order by
And this is where the problem is, I have specified
order by [dbo].[Func_RetentionPriorityStatus](#currentPriority)
order by (select [dbo].[Func_RetentionPriorityStatus](#currentPriority))
order by + (select [dbo].[Func_RetentionPriorityStatus](#currentPriority))
order by convert(varchar, (select dbo.[Func_RetentionPriorityStatus](1)))
order by + convert(varchar, (select dbo.[Func_RetentionPriorityStatus](1)))
But none of them is working. Now I am not even sure if this can be done, and if it, can anyone please tell me what am I doing wrong?
I think you would want to pass the ID to you function as parameter. Then your function will return different values for each row and it would be used in order by.

SQL where clause not getting filtered

I have the following query, but it is not giving any regard to the in the p.created_by =#searchBy where clause, how to correct it so that the results would be filtered according #searchBy too.
ALTER PROC [dbo].[Rptcashcollectionouter] #branchId INT,
#searchBy INT,
#strDate DATETIME=NULL,
#endDate DATETIME=NULL
AS
BEGIN
SELECT DISTINCT p.created_on AS paid_date
FROM reading re
JOIN billing_gen bg ON re.id = bg.reading_id
JOIN customer_registration cr ON bg.account_number = cr.account_number
JOIN payment p ON bg.bill_number = p.bill_number
JOIN customer_category cc ON cr.customer_category_id = cc.id
WHERE p.created_by = #searchBy
AND ( ( #strDate IS NULL )
OR Cast(Floor(Cast(p.created_on AS FLOAT)) AS DATETIME) >=
Cast(Floor(Cast(#strDate AS FLOAT)) AS DATETIME) )
AND ( ( #endDate IS NULL )
OR Cast(Floor(Cast(p.created_on AS FLOAT)) AS DATETIME) <=
Cast(Floor(Cast(#endDate AS FLOAT)) AS DATETIME) )
AND cr.branch_id = #branchId
ORDER BY p.created_on ASC;
END;
Check the value inside your procedure as below.
SELECT #branchId, #searchBy, #strDate,#endDate
And, then try to run the SQL manually with the same value. Also, make sure you have data in your table for your criteria.
Also, what exactly you are trying here ?
Cast(Floor(Cast(p.created_on AS FLOAT)) AS DATETIME)
While executing procedure, make sure you are passing properly value.
Print out all values that are coming (Just for testing).
#searchBy INT is of integer type. But i think "p.created_by =#searchBy" is a type of datetime or date , so it may also conflicts here, or display wrong result. In below line. p.created_by is treating as a datetime or date and #searchby in integer.
WHERE p.created_by = #searchBy

Update table with new value for each row

I need to update a column (type of datetime) in the top 1000 rows my table. However the catch is with each additional row I must increment the GETDATE() by 1 second... something like DATEADD(ss,1,GETDATE())
The only way I know how to do this is something like this:
UPDATE tablename
SET columnname = CASE id
WHEN 1 THEN DATEADD(ss,1,GETDATE())
WHEN 2 THEN DATEADD(ss,2,GETDATE())
...
END
Obviously this is not plausible. Any ideas?
How about using id rather than a constant?
UPDATE tablename
SET columnname = DATEADD(second, id, GETDATE() )
WHERE id <= 1000;
If you want the first 1000 rows (by id), but the id has gaps or other problems, then you can use a CTE:
with toupdate as (
select t.*, row_number() over (order by id) as seqnum
from tablename
)
update toupdate
set columnname = dateadd(second, seqnum, getdate())
where seqnum <= 1000;
I don't know what your ID is like, and I'm assuming you have at least SQL Server 2008 or else ROW_NUMBER() won't work.
Note: I did top 2 to show you that you that the top works. You can change it to top 1000 for your actual query.
DECLARE #table TABLE (ID int, columnName DATETIME);
INSERT INTO #table(ID)
VALUES(1),(2),(3);
UPDATE #table
SET columnName = DATEADD(SECOND,B.row_num,GETDATE())
FROM #table A
INNER JOIN
(
SELECT TOP 2 *, ROW_NUMBER() OVER (ORDER BY ID) row_num
FROM #table
ORDER BY ID
) B
ON A.ID = B.ID
SELECT *
FROM #table
Results:
ID columnName
----------- -----------------------
1 2015-03-31 13:11:59.760
2 2015-03-31 13:12:00.760
3 NULL
You don't make explicit the SQL Server version you're using, so I will assume SQL Server 2005 or above. I believe the WAITFOR DELAY command would be a good option to keep adding 1 second to each rows of the datetime column.
See this example:
-- Create temp table
CREATE TABLE #Client
(
RecordID int identity(1,1),
[Name] nvarchar(100) not null,
PurchaseDate datetime null
)
-- Fill in temp table with example values
INSERT INTO #Client
VALUES ( 'Jhon', NULL)
INSERT INTO #Client
VALUES ( 'Martha', NULL)
INSERT INTO #Client
VALUES ( 'Jimmy', NULL)
-- Create local variables
DECLARE #currentRecordId int,
#currentName nvarchar(100)
-- Create cursor
DECLARE ClientsCursor CURSOR FOR
SELECT RecordID,
[Name]
FROM #Client
OPEN ClientsCursor
FETCH FROM ClientsCursor INTO #currentRecordId, #currentName
-- Check ##FETCH_STATUS to see if there are any more rows to fetch.
WHILE ##FETCH_STATUS = 0
BEGIN
UPDATE #Client
SET PurchaseDate = DATEADD(ss,1,GETDATE())
WHERE RecordID = #currentRecordId
AND [Name] = #currentName
WAITFOR DELAY '00:00:01.000'
FETCH NEXT FROM ClientsCursor INTO #currentRecordId, #currentName
END
CLOSE ClientsCursor;
DEALLOCATE ClientsCursor;
SELECT *
FROM #Client
And here's the result:
1 Jhon 2015-03-31 15:20:04.477
2 Martha 2015-03-31 15:20:05.473
3 Jimmy 2015-03-31 15:20:06.470
Hope you find this answer helpful
This should be what you need (at least a guidline)
DELIMITER $$
CREATE PROCEDURE ADDTIME()
BEGIN
DECLARE i INT Default 1 ;
simple_loop: LOOP
UPDATE tablename SET columnname = DATE_ADD(NOW(), INTERVAL i SECOND) where rownumber = i
SET i=i+1;
IF i=1001 THEN
LEAVE simple_loop;
END IF;
END LOOP simple_loop;
END $$
To call that stored procedure use
CALL ADDTIME()

Loop through a recordset and use the result to do another SQL select and return the results

I am completely new to stored procedure. This time, I need to create a stored procedure in MS SQL.
Let's say I have the following table.
Table name: ListOfProducts
--------------------------
SomeID, ProductID
34, 4
35, 8
35, 11
How do I pass in a SomeID. Use this SomeID to select a recordset from table, ListOfProducts. Then loop through this record set.
Let's say I pass in SomeID = 35.
So, the record set will return 2 records with SomeID 35. In the loop, I will get ProductID 8 and 11, which will be used to do another select from another table.
The stored procedure should return the results from the 2nd select.
How can I do this in MS SQL stored procedure?
Sorry, for this newbie question. Thanks for any help.
If you want looping through the records. You can do like:
--Container to Insert Id which are to be iterated
Declare #temp1 Table
(
tempId int
)
--Container to Insert records in the inner select for final output
Declare #FinalTable Table
(
Id int,
ProductId int
)
Insert into #temp1
Select Distinct SomeId From YourTable
-- Keep track of #temp1 record processing
Declare #Id int
While((Select Count(*) From #temp1)>0)
Begin
Set #Id=(Select Top 1 tempId From #temp1)
Insert Into #FinalTable
Select SomeId,ProductId From ListOfProducts Where Id=#Id
Delete #temp1 Where tempId=#Id
End
Select * From #FinalTable
There is probably no point in writing an explicit loop if you don't need to preform some action on the products that can't be done on the whole set. SQL Server can handle stuff like this much better on its own. I don't know what your tables look like, but you should try something that looks more like this.
CREATE PROC dbo.yourProcName
#SomeID int
AS
BEGIN
SELECT
P.ProductId,
P.ProductName
FROM
Product P
JOIN
ListOfProducts LOP
ON LOP.ProductId = P.ProductId
WHERE
LOP.SomeId = #SomeID
END
I had to do something similar in order to extract hours from a select resultset start/end times and then create a new table iterating each hour.
DECLARE #tCalendar TABLE
(
RequestedFor VARCHAR(50),
MeetingType VARCHAR(50),
RoomName VARCHAR(MAX),
StartTime DATETIME,
EndTime DATETIME
)
INSERT INTO #tCalendar(RequestedFor,MeetingType,RoomName,StartTime,EndTime)
SELECT req as requestedfor
,meet as meetingtype
,room as rooms
,start as starttime
,end as endtime
--,u.datetime2 as endtime
FROM mytable
DECLARE #tCalendarHours TABLE
(
RequestedFor VARCHAR(50),
MeetingType VARCHAR(50),
RoomName VARCHAR(50),
Hour INT
)
DECLARE #StartHour INT,#EndHour INT, #StartTime DATETIME, #EndTime DATETIME
WHILE ((SELECT COUNT(*) FROM #tCalendar) > 0)
BEGIN
SET #StartTime = (SELECT TOP 1 StartTime FROM #tCalendar)
SET #EndTime = (SELECT TOP 1 EndTime FROM #tCalendar)
SET #StartHour = (SELECT TOP 1 DATEPART(HOUR,DATEADD(HOUR,0,StartTime)) FROM #tCalendar)
SET #EndHour = (SELECT TOP 1 DATEPART(HOUR,DATEADD(HOUR,0,EndTime)) FROM #tCalendar)
WHILE #StartHour <= #EndHour
BEGIN
INSERT INTO #tCalendarHours
SELECT RequestedFor,MeetingType,RoomName,#StartHour FROM #tCalendar WHERE StartTime = #StartTime AND EndTime = #EndTime
SET #StartHour = #StartHour + 1
END
DELETE #tCalendar WHERE StartTime = #StartTime AND EndTime = #EndTime
END
Do something like this:
Declare #ID int
SET #ID = 35
SELECT
p.SomeID
,p.ProductID
FROM ListOfProducts p
WHERE p.SomeID = #ID
-----------------------
--Or if you have to join to get it
Declare #ID int
SET #ID = 35
SELECT
c.SomeID
,p.ProductID
,p.ProductName
FROM ListOfProducts p
INNER JOIN categories c on p.ProductID = c.SomeID
WHERE p.SomeID = #ID
You can use option with WHILE loop and BREAK/CONTINUE keywords
CREATE PROC dbo.yourProcName
#SomeID int
AS
BEGIN
IF OBJECT_ID('tempdb.dbo.#resultTable') IS NOT NULL DROP TABLE dbo.#resultTable
CREATE TABLE dbo.#resultTable
(Col1 int, Col2 int)
DECLARE #ProductID int = 0
WHILE(1=1)
BEGIN
SELECT #ProductID = MIN(ProductID)
FROM ListOfProducts
WHERE SomeID = #SomeID AND ProductID > #ProductID
IF #ProductID IS NULL
BREAK
ELSE
INSERT dbo.#resultTable
SELECT Col1, Col2
FROM dbo.yourSearchTable
WHERE ProductID = #ProductID
CONTINUE
END
SELECT *
FROM dbo.#resultTable
END
Demo on SQLFiddle

sql server 2008, cannot use order by in subquery

here's my sql server 2008 stored procedure.
ALTER PROCEDURE [dbo].[GetSharedSmoothies]
#Page INT ,
#Status INT ,
#ItemPerPage INT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #X INT
DECLARE #Y INT
SET #X = ( #Page - 1 ) * #ItemPerPage
SET #Y = #Page * #ItemPerPage
SELECT *
FROM ( SELECT S.* ,
U.Avatar ,
U.Displayname ,
( SELECT COUNT(Id)
FROM Vote
WHERE Vote.SmoothieId = S.Id
) AS Votes ,
ROW_NUMBER() OVER ( ORDER BY S.Id ) rownum
FROM dbo.Smoothie AS S
INNER JOIN dbo.[User] AS U ON S.UserId = U.Id
WHERE S.IsPublic = 1
AND S.Status = 3
AND S.UserId > 0
-- ORDER BY S.CreatedDate DESC
) seq
WHERE seq.rownum BETWEEN #X AND #Y
ORDER BY seq.rownum
END
in my code, you will see I comment out the order by
-- ORDER BY S.CreatedDate DESC
because order by will not work in subquery. i need to show the lastest one on the top. is there a way I can use order by in my code?
You may add S.CreatedDate within the Row_NUMBER()
ROW_NUMBER() OVER (PARTITION BY S.Id ORDER BY S.CreatedDate DESC) AS RowNum
That's right. It is not allowed, because it will do nothing.
Having the latest one at the top in the subquery will do nothing to the result set using the subquery.
Add the needed column to the result set ORDER BY:
ORDER BY seq.CreatedDate DESC, seq.rownum
Or:
ORDER BY seq.rownum, seq.CreatedDate DESC