How to make order by clause dynamic - sql

I am using SQL Server 2008, I want sorted data based on input column name and sort order (asc/ desc).How to make below query dynamic?
DECLARE #iColumnName VARCHAR(24);
DECLARE #iSortOrder VARCHAR(10);
SET #iColumnName = 'ReceiptLocation'; -- ReceiptLocation/DeliverLocation/NominationNbr
SET #iSortOrder = 'DESC'; -- DESC / ASC
SELECT sum(NominationNbr)
,sum(ReceiptLocation)
,sum(DeliverLocation)
FROM tables
GROUP BY NominationNbr
,ReceiptLocation
,DeliverLocation
ORDER BY CASE #iColumnName
WHEN 'ReceiptLocation'
THEN ReceiptLocation
WHEN 'DeliverLocation'
THEN DeliverLocation
ELSE NominationNbr
END
CASE #iSortOrder
WHEN 'DESC'
THEN DESC
ELSE ASC
END

You need to combine the two. I would suggest this rather clunky code:
ORDER BY (CASE WHEN #iColumnName = 'ReceiptLocation' AND #iSortOrder = 'DESC'
THEN ReceiptLocation
END) DESC,
(CASE WHEN #iColumnName = 'ReceiptLocation'
THEN ReceiptLocation
END) ASC,
(CASE WHEN #iColumnName = 'DeliverLocation' AND #iSortOrder = 'DESC'
THEN DeliverLocation
END) DESC,
(CASE WHEN #iColumnName = 'DeliverLocation'
THEN DeliverLocation
END) ASC,
(CASE WHEN #iSortOrder = 'DESC'
THEN NominationNbr
END) DESC,
NominationNbr ASC
Each CASE statement is a separate order key. However, the values are NULL if they do not match, so the key doesn't do anything with no match.
You can also implement this using dynamic SQL. That can be more effective if you have a simple query and indexes that can be used for the ORDER BY.

Related

How to order by multiple columns in sql

I need help ordering my results, probably with a combination of case statements. Please see image showing existing output, conditions, desired output. Exhibited in Excel for ease, but actually done in SQL. The ORDER BY clause is what I need help on.
select
distinct
CONCAT(selection.Selid,' - ' ,Selection.Name,' - ', DevOfficer.Description,' - ', SchemeCount.[Number of Schemes],' Schemes - ',case when selection.Project=1 then '[Project] ' when selection.Project=0 then '[Selection] ' else 'Error' end,convert(varchar,selection.lastupdated,103)) [String]
, selection.selid
, selection.lastupdated
,case when selection.Project=1 then '[Project]'
when selection.Project=0 then '[Selection]'
else 'Error' end
,selection.lastupdated
from selection
inner join SelScheme on selection.SelID =selscheme.SelID
inner join DevOfficer on selection.DevOfficer = DevOfficer.DevOfficerID
inner join (select selscheme.selid ,count(selscheme.SchemeID) [Number of Schemes] from SelScheme group by SelScheme.SelID) SchemeCount on schemecount.SelID = Selection.SelID
where selection.masterselid = 0
order by
--selection.lastupdated desc,
case
when selection.Project=1 then '[Project]'
when selection.Project=0 then '[Selection]'
else 'Error' End Desc
,selid desc
,selection.lastupdated
this is the order by you need:
order by type desc,
id desc,
case when isdate(last_updated_date) then last_updated_date else 0 end desc
use asc and desc modifiers, for each order-by column you need
* Answered before code snippet added to OP question*
Although I'm unsure of SQL table name and even the actual SQL field names, the below will work.
You need to list all fields in the ORDER BY based on their priority;
SELECT
[ID]
,[Type]
,[Last Updated Date]
FROM [SCHEMA].[TABLENAME]
ORDER BY [Type] ASC, [ID] DESC, [Last Updated Date] DESC

How to sort data in stored procedure with column positions of select statement with CASE

This is not working
ORDER BY
CASE
WHEN #OrderBy = 'EndDateInDays' AND #OrderByDirection = 'D'
THEN 10
END DESC,
CASE
WHEN #OrderBy = 'EndDateInDays' AND #OrderByDirection != 'D'
THEN 10
END
Where this one is working
ORDER BY 10 DESC
As per documentation,
ORDER BY order_by_expression
order_by_expression Specifies a column or expression on which to sort
the query result set. A sort column can be specified as a name or
column alias, or a nonnegative integer representing the position of
the column in the select list.
Source: SELECT - ORDER BY Clause (Transact-SQL)
You are specifying an expression, hence the SQL Server does not sort by value in column #10. Instead it sorts your rows by a constant value '10', which results in no sort being performed.
Suggested solution
Move all complex columns into a CROSS APPLY sub-query and add another CROSS APPLY with a sorting column:
SELECT F.Id
,cols.Favourite
,F.Agent
,F.Name
,cols.DatePublished
,UF.ToolTip
,F.CreationDate
FROM MyTable F
INNER JOIN MyTable2 UF
ON f.Id = UF.Id
CROSS APPLY (
SELECT (CASE WHEN UFF.[Id] IS NULL THEN CONVERT(BIT, 0) ELSE CONVERT(BIT, 1) END) AS Favourite
,CONVERT(Datetime, F.[Date] , 103) AS DatePublished
) cols
CROSS APPLY (
SELECT -- Make sure to correctly cast all numeric and date values to text
CASE WHEN #OrderBy = 'EndDateInDays' THEN CONVERT(VARCHAR(100), F.[EndDateDate], 126)
WHEN #OrderBy = 'Name' THEN F.Name
WHEN #OrderBy = 'DatePublished' THEN cols.DatePublished
ELSE CONVERT(VARCHAR(100), F.Id) -- This is default sort
END AS [SortCol]
) sort
ORDER BY
CASE WHEN #OrderByDirection = 'D' THEN sort.[SortCol] END DESC
,CASE WHEN #OrderByDirection != 'D' THEN sort.[SortCol] END
;
You can use the column alias. Let's say it is EndDateInDays:
ORDER BY (CASE WHEN #OrderBy = 'EndDateInDays' AND #OrderByDirection = 'D'
THEN EndDateInDays
END) DESC,
(CASE WHEN #OrderBy = 'EndDateInDays' AND #OrderByDirection <> 'D'
THEN EndDateInDays
END)
If EndDateInDays is a number (as suggested by the name), you could do:
ORDER BY (CASE WHEN #OrderBy = 'EndDateInDays' AND #OrderByDirection = 'D'
THEN - EndDateInDays
WHEN #OrderBy = 'EndDateInDays' AND #OrderByDirection <> 'D'
THEN EndDateInDays
END)
Also, be careful if you start combining multiple columns. It is better to have a separate CASE expression for each column to prevent inadvertent type mismatching.

SQL: Order first by table.column = 'matchingstring', then order everything else alphabetically?

I'm currently using MySQL and I want to order my book_versions records where the book_versions.name = 'paperback' show up first, and then the rest of the book_versions (book_versions.name != 'paperback') show. How would I accomplish this?
order by case when book_versions.name = 'paperback' then 0 else 1 end,
book_versions.name, -- remove this line if other names should not be ordered
book_versions.isbn
See sqlFiddle to see the difference
in mysql, you can also use field
order by field(book_versions.name, 'paperback') DESC,
book_versions.name ASC,
book_versions.isbn ASC
Try:
ORDER BY
CASE WHEN book_versions.name = 'paperback' THEN 0 ELSE 1 END, -- puts paperbacks first (because paperbacks cause this to =0, which is ordered before 1)
book_versions.name -- then order alphabetically

0 come last when sorting ascending

I am trying to select from one table a list of products ordered by price, year, name, and others....
The problem is that I must make zero values come last when sorting ascending.
My code is:
SELECT * FROM Product P
ORDER BY CASE WHEN #OrderBy='Date ASC' THEN Date END ASC,
CASE WHEN #OrderBy='Price ASC' THEN Price END ASC,
CASE WHEN #OrderBy='Title ASC' THEN Title END ASC,
CASE WHEN #OrderBy='' THEN Match END
This works but don't put the zero at the bottom of the list.
So, I tried to transform it (see next code), but it gave me the error Incorrect syntax near ','.
SELECT * FROM Product P
ORDER BY CASE WHEN #OrderBy='Price ASC' THEN
(case A.Price WHEN 0 THEN 1 ELSE 0 END,A.Price )
END ASC
I appreciate any help
You can do it by testing for price-ordering twice:
SELECT * FROM Product P
ORDER BY CASE WHEN #OrderBy='Date ASC' THEN Date END ASC,
CASE WHEN #OrderBy='Price ASC' THEN CASE WHEN Price = 0 THEN 1 ELSE 0 END ASC,
CASE WHEN #OrderBy='Price ASC' THEN Price END ASC,
CASE WHEN #OrderBy='Title ASC' THEN Title END ASC,
CASE WHEN #OrderBy='' THEN Match END
By the way, the implicit value of the case expression when #orderBy doesn't equal the string is null. When the sort column contains all nulls, it effectively disables sorting for that attribute.
I would suggest using a large dummy price:
ORDER BY CASE WHEN #OrderBy='Price ASC' THEN 99999999 ELSE A.Price END ASC
or if you DBMS supports NULLS LAST:
ORDER BY CASE WHEN #OrderBy='Price ASC' THEN NULLIF(A.Price,0) END ASC NULLS LAST
You can try with this syntax:
SELECT *,
CASE WHEN #OrderBy = 'Price ASC' AND Price = 0 THEN 1 ELSE 0 END AS OrderPriceZeroLast
FROM Product P
ORDER BY OrderPriceZeroLast,
CASE WHEN #OrderBy = 'Date ASC' THEN Date END ASC,
CASE WHEN #OrderBy = 'Price ASC' THEN Price END ASC,
CASE WHEN #OrderBy = 'Title ASC' THEN Title END ASC,
CASE WHEN #OrderBy = '' THEN Match END
I can't add comments yet. There is bug in your code
SELECT * FROM Product P
ORDER BY CASE WHEN #OrderBy='Price ASC' THEN
(case A.Price WHEN 0 THEN 1 ELSE 0 END,A.Price )
END ASC
Shoud be smth like
SELECT *
FROM Product P
ORDER BY
CASE
WHEN #OrderBy='Price ASC'
THEN
CASE A.Price
WHEN 0
THEN 1
ELSE 0
END
END,
A.Price
This may seem like a hack but you could create a new column on the result set on the fly. Like this:
SELECT *, [name of column that may contain zeroes] as foo WHERE /* rest of your code */
Then you could sort DESC on foo and ASC on the rest. Just remember to now show foo to the user. Also notice that yes, you'll get the same column in the result set twice. You also have to use a CASE to turn all non-zero values into 1 (or some other constant value).
Try this to sort 0 value to last, when there are 0,1,2,... values are in field.
It will bring 1, 2 ,... and 0 to last order.
select * from Product
order by
case
when OrderBy = 0
then -1
else 0
end,
OrderBy desc
-Chirag

T SQL Chain join 4 tables

Here is the scenario:
I have tables:
Articles (articleId, userId, title, datePosted)
ArticleTranslations (languageId, articleId, title) (not so important for this case, but I am showing anyway)
ArticleSections (articleSectionId, articleId, sectionId)
sections (sectionId, content, ...)
sectionsAdditionalInfo (sectionId, isApproved)
What I am doing is selecting some articles from articles by userId in a way:
SELECT article.articleId, article.userId, ArticleTranslations.title, article.datePosted
FROM Articles
LEFT OUTER JOIN ArticleTranslations ON Article.articleId= ArticlesTranslations.articleId AND ArticlesTranslations.languageId=#languageId
WHERE Articloes.userId=#userId
ORDER BY
CASE WHEN #sortDirection = 'DD' THEN datePosted END DESC, -- by date posted
CASE WHEN #sortDirection = 'DA' THEN datePosted END,
CASE WHEN #sortDirection = 'ND' THEN title END DESC, -- sort by name
CASE WHEN #sortDirection = 'NA' THEN title END,
CASE WHEN #sortDirection = 'SD' THEN ArticleTranslations.isApproved END DESC, -- is article aproved?
CASE WHEN #sortDirection = 'SA' THEN ArticleTranslations.isApproved END,
CASE WHEN #sortDirection = 'ID' THEN areAllSectionsApproved END DESC, -- sort by information if sections are all approved - within the article?
CASE WHEN #sortDirection = 'IA' THEN areAllSectionsApproved END
Please bare in mind I left out some of the info in order for my question to be more understandable.
Now, what I would like to do is select another attribute (for each article returned in SQL above): areAllArticleSectionsApproved
I have assembled SQL separately, but I would like this to be returned for every row:
SELECT CASE WHEN COUNT(sectionsAdditionalInfo.sectionId) > 0 THEN 0 ELSE 1 END AS areAllSectionsApproved
FROM ArticleSections
LEFT OUTER JOIN sectionsAdditionalInfo ON ArticleSections.sectionId = sectionsAdditionalInfo.sectionId
WHERE articleId=#articleId AND sectionsAdditionalInfo.isApproved=0
I have tried nesting this SQL, in a way:
SELECT (outer SQL) .....
a.*(
nested SQL - second one I posted here
) as a
but it didn't work at all.
I am using SQL server 2008.
Any hint on how to solve this would be greatly appreciated ;)
Without fully understanding your data and your desired results, something like this should work using your above query and joining on your second query as a subquery:
SELECT article.articleId,
article.userId,
ArticleTranslations.title,
article.datePosted,
t.areAllSectionsApproved
FROM Articles
LEFT OUTER JOIN ArticleTranslations
ON Article.articleId= ArticlesTranslations.articleId
AND ArticlesTranslations.languageId=#languageId
LEFT JOIN (
SELECT
articleId,
CASE
WHEN COUNT(sectionsAdditionalInfo.sectionId) > 0
THEN 0
ELSE 1
END AS areAllSectionsApproved
FROM ArticleSections
LEFT OUTER JOIN sectionsAdditionalInfo
ON ArticleSections.sectionId = sectionsAdditionalInfo.sectionId
WHERE sectionsAdditionalInfo.isApproved=0
GROUP BY articleId
) t ON articles.articleId = t.articleId
WHERE Articloes.userId=#userId
ORDER BY
CASE WHEN #sortDirection = 'DD' THEN datePosted END DESC, -- by date posted
CASE WHEN #sortDirection = 'DA' THEN datePosted END,
CASE WHEN #sortDirection = 'ND' THEN title END DESC, -- sort by name
CASE WHEN #sortDirection = 'NA' THEN title END,
CASE WHEN #sortDirection = 'SD' THEN ArticleTranslations.isApproved END DESC, -- is article aproved?
CASE WHEN #sortDirection = 'SA' THEN ArticleTranslations.isApproved END,
CASE WHEN #sortDirection = 'ID' THEN areAllSectionsApproved END DESC, -- sort by information if sections are all approved - within the article?
CASE WHEN #sortDirection = 'IA' THEN areAllSectionsApproved END
Good luck.