SQL concatenation with ORDER BY clause - sql

I want to concatenate a variable name into my SQL query for order by such as the following..
I basically don't wan to use dynamic sql and exec and sp_executesql.
DECLARE #Order VARCHAR(1000)
SET #Order = 'CO_ID DESC'
print #Order
select * From contact where co_username= 'sandeepshm' order by #Order+#Order
select * From contact where co_username= 'sandeepshm' order by CO_ID DESC

you can use Case statement but not variable
something like this:
declare #sortByCO_id bit
declare #sortbyCo_Username bit
select
*
from contact
order by
case when #sortByCO_id = 1 then CO_Id else NULL end DESC
,case when sortbyCo_Username = 1 then Co_Username else NULL end

Related

Output Parameter incorrect syntax near SELECT [duplicate]

This question already has answers here:
How to set variable from a SQL query?
(9 answers)
Closed 3 years ago.
Im working on stored procedure in SQL SERVER. From this query im getting list of Tests with pagination (skip and size). I want to get total count of Tests in Tests table. I want to do it by OUTPUT parameter. My query looks like this:
#size INT,
#skip INT,
#orderDir VARCHAR(4),
#orderBy VARCHAR(20),
#totalCount INT OUTPUT
AS
BEGIN
CREATE TABLE #Ids (
TestId INT
);
INSERT INTO #Ids SELECT TestId FROM Tests;
SELECT SomeStuff
FROM Tests t JOIN TestLines tl ON t.TestId = tl.TestId
GROUP BY SomeStuff
ORDER BY
CASE WHEN #orderDir = 'asc'AND #orderBy = 'TestId' THEN t.TestId END,
CASE WHEN #orderDir = 'desc' AND #orderBy = 'TestId' THEN t.TestId END DESC,
-- more case when here
OFFSET #skip ROWS
FETCH NEXT #size ROWS ONLY;
SET #totalCount = SELECT COUNT(TestId) FROM #Ids
DROP TABLE #Ids;
END
I cant create procedure with query above. On line SET #totalCount = SELECT COUNT(TestId) FROM #Ids i get this error:
Incorrect syntax near 'SELECT'
My qustion is, how to make it work properly? How to get that value as OUTPUT from stored procedure?
Use SELECT directly :
SELECT #totalCount = COUNT(TestId)
FROM #Ids;
Or you can use subquery :
SET #totalCount = (SELECT COUNT(TestId) FROM #Ids)
You should fix by this way
Using SELECT
SELECT #totalCount = COUNT(TestId) FROM #Ids
Using SET
SET #totalCount = (SELECT COUNT(TestId) FROM #Ids)

Stored procedure to sort one row with other rows places

I would like to add this to a parameterized stored procedure. I have code to sort it like that but it is just a select statement.
SELECT *
FROM [AdventureWorks2014].[Person].[CountryRegion]
ORDER BY
CASE CountryRegionCode
WHEN 'GB' THEN '1'
WHEN 'BR' THEN '2'
ELSE Name
END
GO
In these pictures everything could be clear what I would like to happen. Before and after sort. Thanks!
I would use this personally. to sort by name after the preference sort
CREATE PROCEDURE yourProcedureName
#RegionCode nvarchar(2)
AS
--your select
ORDER BY
CASE CountryRegionCode
WHEN #RegionCode THEN 1
ELSE 2
END,
Name
Another way to do it
CREATE PROCEDURE yourProcedureName #RegionCode nvarchar(2)
AS
SELECT *
FROM [AdventureWorks2014].[Person].[CountryRegion]
WHERE CountryRegionCode = #RegionCode
UNION ALL
SELECT *
FROM [AdventureWorks2014].[Person].[CountryRegion]
WHERE CountryRegionCode = #RegionCode
--Any order by you need
END
GO
And call it using :
EXEC yourProcedureName #RegionCode = 'GB'
This was the result what I needed and what worked to replace one record under the specified parameter record.
ALTER PROCEDURE something
#CountryRegionCode nvarchar(3),
#Name nvarchar(50)
AS
SELECT Name,CountryRegionCode,ModifiedDate FROM [AdventureWorks2014].[Person].[CountryRegion]
ORDER BY
CASE CountryRegionCode
WHEN #CountryRegionCode THEN #Name
ELSE Name
END

Select Columns based on value of another variable?

Suppose I have this code:
DECLARE #IncludeDuplicateAddressIDs tinyint
-- Value of #IncludeDuplicateAddressIDs set here ! --
IF(#IncludeDuplicateAddressIDs = 1)
SELECT AddressIds FROM RTS.ADDRESSES
ELSE
SELECT DISTINCT(AddressIds) FROM RTS.ADDRESSES
Can I combine the two SELECT statements into one ? That is, using just one SELECT, I show either all AddressIDs, or just one of each ?
You can do it like this:
SELECT AddressIds FROM RTS.ADDRESSES WHERE #IncludeDuplicateAddressIDs = 1
UNION ALL
SELECT DISTINCT(AddressIds) FROM RTS.ADDRESSES WHERE #IncludeDuplicateAddressIDs <> 1
Since the WHERE clauses are mutually exclusive, only one of the UNION-ed queries would return some rows; the other query will return nothing.
If the table contains a unique column then you can use this option (assuming the unique column's name to be Id):
DECLARE #IncludeDuplicateAddressIDs tinyint = 1
SELECT AddressIds
FROM RTS.ADDRESSES
GROUP BY CASE WHEN #IncludeDuplicateAddressIDs = 1
THEN Id ELSE AddressIds END, AddressIds
Demo on SQLFiddle
You could include the number of duplicate addresses.
SELECT AddressIDs, count(*) as cnt
FROM RTS.ADDRESSES
GROUP BY AdressIDs
Try this one -
DECLARE #IncludeDuplicateAddressIDs TINYINT
SELECT #IncludeDuplicateAddressIDs = 0
SELECT t.AddressIds
FROM (
SELECT
AddressIds
, cnt = ROW_NUMBER() OVER (PARTITION BY AddressIds ORDER BY AddressIds)
FROM RTS.ADDRESSES
) t
WHERE #IncludeDuplicateAddressIDs = 1
OR (#IncludeDuplicateAddressIDs != 1 AND cnt = 1)
In this query, the UNION or DISTINCT construction is not used. Therefore, SQL can generate a more efficient query plan.
You can use sp_executesql dynamic sql to form your query as a string (and set as many columns as you want) and then execute it:
DECLARE #IncludeDuplicateAddressIDs tinyint
DECLARE #Columns varchar(500);
IF(#IncludeDuplicateAddressIDs = 1) SET #Columns='AddressIds'
ELSE SET #Columns='DISTINCT(AddressIds)'
EXECUTE sp_executesql N'SELECT ' + #Columns + 'AddressIds FROM RTS.ADDRESSES';

Dynamic order by without using dynamic sql?

I have the following stored procedure which can be sorted ascending and descending on TemplateName,CreatedOn and UploadedBy. The following SP when runs doesnot sort records.if i replace 2,3,4 by columnname, i got an error message "Conversion failed when converting the nvarchar value 'Test Template' to data type int.".Please suggest how to achieve sorting.
CREATE PROCEDURE [dbo].[usp_SEL_GetRenderingTemplate]
(
#facilityID INT,
#sortOrder VARCHAR(5),
#sortExpression VARCHAR(100),
#errorCode INT OUTPUT
)
AS
BEGIN
SET NOCOUNT ON ;
BEGIN TRY
SET #sortOrder = CASE #sortOrder
WHEN 'Ascending' THEN 'ASC'
WHEN 'Descending' THEN 'DESC'
ELSE 'ASC'
END
SELECT TemplateID,
TemplateName,
CreatedOn,
( [user].LastName + ' ' + [user].FirstName ) AS UploadedBy
FROM Templates
INNER JOIN [user] ON [user].UserID = Templates.CreatedBy
WHERE facilityid = #facilityID
ORDER BY CASE WHEN #sortExpression = 'TemplateName'
AND #sortOrder = 'ASC' THEN 2
WHEN #sortExpression = 'CreatedOn'
AND #sortOrder = 'ASC' THEN 3
WHEN #sortExpression = 'UploadedBy'
AND #sortOrder = 'ASC' THEN 4
END ASC,
CASE WHEN #sortExpression = 'TemplateName'
AND #sortOrder = 'DESC' THEN 2
WHEN #sortExpression = 'CreatedOn'
AND #sortOrder = 'DESC' THEN 3
WHEN #sortExpression = 'UploadedBy'
AND #sortOrder = 'DESC' THEN 4
END DESC
SET #errorCode = 0
END TRY
BEGIN CATCH
SET #errorCode = -1
DECLARE #errorMsg AS VARCHAR(MAX)
DECLARE #utcDate AS DATETIME
SET #errorMsg = CAST(ERROR_MESSAGE() AS VARCHAR(MAX))
SET #utcDate = CAST(GETUTCDATE() AS DATETIME)
EXEC usp_INS_LogException 'usp_SEL_GetFacilityWorkTypeList',
#errorMsg, #utcDate
END CATCH
END
The dynamic ordering has to be of the same datatype. Here is an example of something that I use to case order three different datatypes - integer, date (ascending and descending), and string. This may not work for your situation, but at least you can see some techniques for casting to a common datatype.
...
ORDER BY
Case Parent.RankTypeID
When 0 Then dbo.Documents.Rank
When 1 Then Convert(int, dbo.Documents.DateStart, 112)
When 2 Then (1 - Convert(int, dbo.Documents.DateStart, 112))
When 3 Then Cast(dbo.Documents.Title as sql_variant)
End
Note:
112 is a date format of YYYYMMDD - useful for ordering.
We perform a similar sort of dynamic ordering in one of our products.
The only real different to your code is firstly, we don't use the live join.
We create a temporary table, so we can perform paging, and then apply the order.
We also use ints for ordering, cuts down on the overhead of string comparison.
It does make your SQL a bit longer, but this approach is definitely faster for the Query Optimiser. Also, and more importantly, I don't think you can mix types in Switch blocks, for ordering, so to follow your original code, you'd have to CONVERT all your data to the same time, which defeats the object :(
So you'd have
DECLARE #temp TABLE(ID int identity(1,1), TemplateID int, TemplateName nvarchar(100), CreatedOn datetime, UploadedBy nvarchar(100))
INSERT INTO #temp(TemplateID, TemplateName, CreatedOn, UploadedBy)
SELECT TemplateID,
TemplateName,
CreatedOn,
( [user].LastName + ' ' + [user].FirstName ) AS UploadedBy
FROM Templates
INNER JOIN [user] ON [user].UserID = Templates.CreatedBy
WHERE facilityid = #facilityID
Then:
IF #SortOrder = 1 --'ASC'
BEGIN
IF #sort = 2
Select *
From #Temp
Order by TemplateName ASC
ELSE IF #sort = 3
Select *
From #Temp
Order By CreatedBy ASC
-- and so on...
END
ELSE -- descending
BEGIN
-- Ad. Inf.
END
Delete
From #Temp
WHERE ID < #pageStart or ID > #pageStart + #pageSize
The following SP when runs doesnot
sort records.if i replace 2,3,4 by
columnname,
You shouldn't replace 2, 3, 4 in the ORDER BY clause by the column name. When you call the procedure just pass the column you want to sort by as the 3rd parameter.
EXEC [dbo].[usp_SEL_GetRenderingTemplate] 1,'Ascending','CreatedOn',#vErrorCode
The CASE will then evaluate the query to something like
...ORDER BY 3 DESC
which sorts the query by the 3rd column in the SELECT clause (i.e. CreatedOn)
The problem is that in CASE clauses the return values must have the same data type.
You can work around this problem by using multiple CASE statements http://www.extremeexperts.com/sql/articles/CASEinORDER.aspx
Or casting everything to the same data type.

SQL -- Selecting Top 1 with Order by?

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;