Creating custom order by SQL Server - sql-server

Customer wants to display a list of values returned by my query in a specific order. The issue is ordering simply by asc or desc is not giving me what the customer want. DBA doesn't want me to hard code values. Is there a way to custom sort without hard coding values? Because the values will change every year and would have to maintain/update it every year.
Table Structure:
Column: CMN_CodesID (unique), Name (is what I'd like to display in custom order)

something like this.
order by case when Jack then 1
when Apple then 2
when Orange then 3
...
End

You could use dynamic sql in a stored procedure and pass #MyOrderBy into it as a parameter (Added to this example for illustration).
DECLARE #MyOrderBy VARCHAR(300)
SELECT #MyOrderBy = 'case when myfield = ''Jack'' then 1 when myfield = ''Apple'' then 2 when myfield = ''Orange'' then 3 else 4 End'
DECLARE #sSQL VARCHAR(300)
SELECT #sSQL = 'SELECT * FROM mytable ORDER BY ' + #MyOrderBy
EXEC(#sSQL)

Related

Convert CHAR to NUMERIC in SQL

I am trying to set up a database that has the following columns, "number", "full names", "card no", "status". In the last column status there are many types of status, which I would like to have them converted to 0 or 1 to another column within the table, depending on the statuses. The database then, is to be pulled to another application and will use the binary column to give access to a facility.
I have not tried any code on this, still learning.
SELECT TOP 1000 [MemberNo]
,[FirstName]
,[LastName]
,[CardNo]
,[Status]
FROM [GateAccess].[dbo].[GateProxy]
Not sure why ask a question when you havent tried anything...
First you are trying to create a table with following columns not a database.
Here is a link of how to create a table http://www.mysqltutorial.org/mysql-create-table/
Second you dont really need to to convert char to numeric I would personally use CASE
here is a link on case https://www.techonthenet.com/mysql/functions/case.php
Regarding to your 'application and will use the binary column to give access to a facility' it depends on what application is pulling the data.
This may help ...
DECLARE #sampletext VARCHAR(100) = '123456';
SELECT TRY_CONVERT(INT, #sampletext); -- 123456
SELECT TRY_CAST(#sampletext AS INT); -- 123456
SELECT TOP 1000 [MemberNo]
,[FirstName]
,[LastName]
,[CardNo]
,[Status]
-- Try like this for new derived column
,CASE WHEN [Status] = 'yourValue'THEN 1
WHEN [Status] = 'yourOtherValue' THEN 0
END
FROM [GateAccess].[dbo].[GateProxy]

use variable in select - tsql

Hi I have procedure which have parameter(#identFormat)
Example
"GUID"
"LotID|FeatureID"
And now I have Select query which should split this and use as columns.
Moreover result should be back combined.
Example:
Table:
Id LotID FeatureID
2 1 4
3 4 5
4 2 1
and if my #identFormat = "LotID|FeatureID" then it should return
Table:
1|4
4|5
2|1
Actually I have ncharchar #columns 'LotId + "|" + FeatureId'
Is it possible to use this like this:
Select #columns from Table ?
or using dynamic sql
EDIT:
Unfortunately combination of columns can be different. My purpose is send column names to procedure and select this columns from specific table. This is procedure to save data , but if something went wrong I must save this unique combination of columns in second table.
Unfortunatelly, it is not possible. You need to select separately and format the output

Create report using dynamic SQL

I have a table example as such:
State Project Build Type ACTUAL0 ACTUAL1 ACTUAL2
------- ------- ----------- ----------- ----------- ----------
Ohio 154214 Residential 1/5/2013 2/25/2013 7/12/12
Utah 214356 Commercial 7/08/13 6/9/13 7/1/12
I am trying to create a report that takes the column headers beginning with the word actual and get a count of how many dates are less than a specific date. I have a temp table that I create of the column headers beginning with the word actual. This is just and example, there are over 250 columns name actual. So the table looks like this:
MilestoneNmbr
-------------
ACTUAL1
ACTUAL2
ACTUAL3
Now what I think would work is to take the row as a variable for the column header and pass in a date into a function. Here is a function I created:
CREATE FUNCTION [dbo].[GetMSActualCount]
(
#ACTUAL nvarchar(16),
#DATE nvarchar(16)
)
RETURNS int
AS
BEGIN
DECLARE #ACTUALRETURN int
DECLARE #SQL nVarchar(255) =
'SELECT COUNT(' + #ACTUAL + ') AS Expr1
FROM [CASPR_MILESTONES_000-036]
WHERE '+ #ACTUAL +' > ' + #DATE
exec sp_executesql #SQL, N''
SET #ACTUALRETURN = #SQL
-- Return the result of the function
RETURN #ACTUALRETURN
END
If I run the following query:
DECLARE #DATE varchar(20)
SET #DATE = '''1/1/2013'''
SELECT MilestoneNmbr, dbo.getMSActualCount(milestonenmbr,#Date) from #List_CASPR_Milestones
So my error is that I can't use dynamic SQL in a function. With that being so, what can I do? My easy query here I think will turn into hundreds of lines. Is there another easy way I can do this?
EDIT:
My results I am looking for is something like this:
MilestoneNmbr CountofDate
--------------- ------------
ACTUAL1 200
ACTUAL2 344
ACTUAL3 400
You are right you can't use dynamic SQL in a function. There are two answers:
First your table with 250 columns ACTUAL plus a number is a nightmare. You can't use any of the built in stuff that SQL does well to help. You should have two tables. First a projects table that has an ID column plus columns for State, Project, and BuildType. Then a table of ProjectDates with a ProjectID column that references the first table and then a column for ActualDate. Reporting from that should be easy.
Given that you probably can't fix the structure try writing a stored procedure. That can use dynamic SQL. Event better is that your stored procedure can create temp tables like above and then use them to do statistics.
I agree 100% with Charles. If you CAN change the structure this is what I would do:
If possible have a build type table (ID/Build Type), don't have text columns unless you need them as text for something. Anything that can be coded, code it.
The two tables:
project header (Proj_ID (long_int)/State (int or char(2)) / build_type (int)), primary key either Proj_id by itself or a new ID if its not unique (as a PK Proj_id & State would not be too useful as a PK).
Project_date (Proj_ID (same as PK above) / Date_ID (int) / Actual_Date (DateTime))
So your second example would be:
Project_Header:
214356 / UT / 2 (being 1 Residential, 2 Commercial, 3 Industrial ...)
Project_Date:
214356 / 0 / '07/08/13'
214356 / 1 / '06/09/13'
214356 / 2 / '07/01/12'
Latest build date by project would be:
Select 'Actual_date'
from Project_date
where Project_id='nnn'
order by date_id DESC
Limit 1;
Your query would be something like (if the dates are in incremental order):
Select Project_id, max(Date_id)
From Project_date
Group by Project_id
having Actual_date < #date
you can see it's pretty straight forward.
If you CAN'T change the structures but you CAN make new tables I would make an SP that takes that ugly table and generates the Project_Date x times per day ( or you could even tie it to a trigger on inert/update of the first table) and the Project_header once per day (or more often if needed). This would take considerably less time and effort than what you are attempting, plus you could use it for other queries.
To solve this I created a table housing the ACTUAL dates. I then went and looped through each row in the List_ACTUAL table to get the names and select the count of the dates names greater than the variable I pass in. I will be converting this to a PROC. This is how:
DECLARE #MS nvarchar(16)
DECLARE MSLIST CURSOR LOCAL FOR SELECT MilstoneNmbr FROM List_ACTUAL
DECLARE #SQL nvarchar(max)
DECLARE #DATE nvarchar(16)
SET #DATE = '1/1/2013'
CREATE #TEMP (Milestones nvarchar(16), Frozen int)
OPEN MSLIST
FETCH NEXT FROM MSLIST INTO #MS
WHILE ##FETCH_STATUS=0
BEGIN
SELECT #SQL = 'INSERT INTO #TEMP VALUES (''' +#MS+ ''', (Select count(' +#MS+ ') FROM PROJECTDATA WHERE ' +#MS+ ' > ''' + #Date + '''))'
EXEC sp_executesql #SQL, N''
FETCH NEXT FROM MSLIST INTO #MS
END
CLOSE MSLIST
DEALLOCATE MSLIST
Hope this helps someone.

Convert or Cast VARCHAR to INT; Update column

Disclaimer:
I am still learning SQL so I apologize if my question comes off as amateur-ish or is otherwise a very simple answer. I have no formal training. I am teaching myself.
The title may be a bit confusing, as I'm not entirely sure how to word this for a title.
Basically, I want to convert a column in a table thats currently VARCHAR into an INT. The column contains only numbers formatted as such:
00001
00005
02150
These are essentially ID's which will be appended to a Name column later for other purposes. If its necessary to do so, I'd also like to know how to convert the end result INT to VARCHAR for the append portion.
Here's what I have right now:
SELECT CONVERT(INT, LocNo)
It returns the results I expect but I think I need to somehow update the existing LocNo column or otherwise put it in a new column for forward use.
What should I do to achieve this?
Try this
UPDATE TableName
SET LocNo = CONVERT(INT, LocNo)
If you want new column, add new column to table and then do update
ALTER TABLE TableName
ADD NewCol Int Null
UPDATE TableName
SET NewCol = CONVERT(INT, LocNo)
When Selecting and appending to varchar you can do
SELECT CAST(LocNO As VARCHAR) + Name as NameAppended From TableName
If you want 0's back in LocNo/newCol then
SELECT right('00000' + CAST(LocNO As VARCHAR),0) + Name as NameAppended
From TableName
UPDATE YourTable
SET LocNo = CONVERT(INT, LocNo)

How to collate rows to a delimited string in SQL2008R2

We are currently upgrading a current data import process we have written in C#.
As part of the upgrade process, we need to check the results of the import process from the rewrite against the results of the old system.
One of the changes we made was breaking comma-delimited lists into rows in another table. This will enable us to filter results using a simple join.
This is the old schema:
FormNumber MainCategories
1 blue,green,red
2 yellow,red,blue
3 white
Which we normalized to:
FormNumber AttributeId Value
1 1 blue
1 1 green
1 1 red
2 1 yellow
2 1 red
2 1 blue
3 1 white
Now, our next step is to confirm that the results from the two processes are the same. One of these checks is to compare the MainCategories field of the old process with the results from the normalized tables.
This leads us, finally, to the question: How do I create a comma-delimited list of the new schema to compare to the value of the old.
We have tried the XMLPath solution proposed by #Ritesh here: Concatenate many rows into a single text string?
Here is the adapted sql statement:
Select distinct ST2.FormNumber,
(Select ST1.Value + ',' AS [text()]
From cache.ArtifactAttribute ST1
Where ST1.FormNumber= ST2.FormNumber
ORDER BY ST1.FormNumber
For XML PATH ('')) [Values]
From cache.ArtifactAttribute ST2
The problem is the results are not correct. Even though FormNumber 1 only has three entries in the table, the Values column (the dynamically built delimited string) shows incorrect results. Obviously we are not implementing the sql code correctly.
What are we doing wrong?
Here is a way for you to try:
SELECT DISTINCT A.FormNumber, MainCategories
FROM YourTable A
CROSS APPLY (SELECT STUFF((SELECT ',' + Value
FROM YourTable
WHERE FormNumber = A.FormNumber FOR XML PATH('')),1,1,'') MainCategories) B
Though there is the problem where you can't really be sure that the order of the items concatenated is the same as the one you have, since there isn't a column that explictly gives that order. Here is a working SQL Fiddle with this example.
This seems to work fine for me:
DECLARE #s TABLE(FormNumber int, AttributeId int, Value varchar(32));
INSERT #s VALUES
(1,1,'blue'),
(1,1,'green'),
(1,1,'red'),
(2,1,'yellow'),
(2,1,'red'),
(2,1,'blue'),
(3,1,'white');
SELECT ST2.FormNumber, [Values] = STUFF(
(SELECT ',' + ST1.Value AS [text()]
FROM #s ST1
WHERE ST1.FormNumber = ST2.FormNumber
ORDER BY ST1.FormNumber
FOR XML PATH (''),
TYPE).value(N'./text()[1]', N'varchar(max)'), 1, 1, '')
FROM #s ST2 GROUP BY ST2.FormNumber;
Results:
FormNumber
Values
1
blue,green,red
2
yellow,red,blue
3
white
Example db<>fiddle

Resources