Order by in varchar - sql-server

in my table the value is like this in field transectionNO is like
R1000891 R1000892 R1000893 ..... I want that last transactionNo means R1000893 as a result without R . So i can add R + last transactionNo + 1 in to create new transectionNo.
i used below query but sometime this query gives old value so it is creation error.
DECLARE #trnsectionNo varchar(50) ,
#RoundTotal varchar(50) ,
#transactionNo varchar(50)
SELECT top 1 #trnsectionNo = trans_no from Item_Receive ORDER BY trans_no DESC
SET #trnsectionNo= Substring(#trnsectionNo,2,10)
SET #RoundTotal= (ISNULL(#trnsectionNo,0) + 1)
SET #transactionNoNew = 'R'+#RoundTotal
thanks for your help .

Please try this query. I've also created a fiddle for demo:
http://sqlfiddle.com/#!6/e928b/6
Note that changes I made only included casting the substring as INT and applying MAX on it.
DECLARE #trnsectionNo INT ,
#RoundTotal varchar(50) ,
#transactionNoNew varchar(50)
SELECT #trnsectionNo = MAX(CAST(Substring(#trnsectionNo,2,10)AS INT)) from Item_Receive
SET #RoundTotal= (ISNULL(#trnsectionNo,0) + 1)
SET #transactionNoNew = 'R'+#RoundTotal

The problem you have is that you're not converting the number into int, so SQL Server can't do the +1. This is what you should be doing:
DECLARE #trnsectionNo varchar(50) ,
#transactionNoNew varchar(50)
SELECT top 1 #trnsectionNo = trans_no from Item_Receive ORDER BY trans_no DESC
if (##rowcount = 0) set #trnsectionNo = 'R0'
select #transactionNoNew = 'R'+convert(varchar(10), convert(int, Substring(#trnsectionNo,2,10)) + 1)
If you need to have zeros in the number, e.g. R000001 instead of R1, then you'll need to add more logic.

Try Using Stuff
select top 1 #trnsectionNo = trans_no from Item_Receive order by Stuff(trans_no,1,1,'') desc

I'm writing this as an answer as it's too long for a comment.
The other answers here will tell you how to fix your problem by converting your varchar to an int. They're correct within the scope of the question you asked, which in essence was "how do I make this piece of really unsafe code work (most of the time)?"
Why do I say your code is unsafe? Atomicity! If you have two calls to your query close together you will get duplicate numbers. Please read about Atomicity and related concepts here: http://searchsqlserver.techtarget.com/definition/ACID They are critical for writing safe DB Code!
If you want to continue with the code you have, and give yourself a hard life trying to make it safe, you would need to wrap the whole thing in a transaction and hold a lock on your table too. The transaction would also need to include the subsequent insert of your new ID number, which you don't include in your snippet.
But why use a varchar to hold an sequential transaction No. anyway? If you need it to show with a "R" in front that should be in your UI code.
If you want safe code with atomicity and an easy life, just use an int column with the identity attribute. See here for more info: http://www.sqlteam.com/article/understanding-identity-columns
If you need to get the new ID value back to your app, use the SCOPE_IDENTITY() function: https://msdn.microsoft.com/en-gb/library/ms190315(v=sql.110).aspx

Related

IIF with Multiple Case Statements for Computed Column

I'm trying to add this as a Formula (Computed Column) but I'm getting an error message saying it is not valid.
Can anyone see what is wrong with the below formula?
IIF
(
select * from Config where Property = 'AutomaticExpiry' and Value = 1,
case when [ExpiryDate] IS NULL OR sysdatetimeoffset()<[ExpiryDate] then 1 else 0 end,
case when [ExpiryDate] IS NULL then 1 else 0 end
)
From BOL: ALTER TABLE computed_column_definition
computed_column_expression Is an expression that defines the value of
a computed column. A computed column is a virtual column that is not
physically stored in the table but is computed from an expression that
uses other columns in the same table. For example, a computed column
could have the definition: cost AS price * qty. The expression can be
a noncomputed column name, constant, function, variable, and any
combination of these connected by one or more operators. The
expression cannot be a subquery or include an alias data type.
Wrap the login in function. Something like this:
CREATE FUNCTION [dbo].[fn_CustomFunction]
(
#ExpireDate DATETIME2
)
RETURNS BIT
AS
BEGIN;
DECLARE #Value BIT = 0;
IF EXISTS(select * from Config where Property = 'AutomaticExpiry' and Value = 1)
BEGIN;
SET #Value = IIF (sysdatetimeoffset()< #ExpireDate, 1, 0)
RETURN #value;
END;
RETURN IIF(#ExpireDate IS NULL, 1, 0);
END;
GO
--DROP TABLE IF EXISTS dbo.TEST;
CREATE TABLE dbo.TEST
(
[ID] INT IDENTITY(1,1)
,[ExpireDate] DATETIME2
,ComputeColumn AS [dbo].[fn_CustomFunction] ([ExpireDate])
)
GO
INSERT INTO dbo.TEst (ExpireDate)
VALUES ('2019-01-01')
,('2018-01-01')
,(NULL);
SELECT *
FROM dbo.Test;
Youre trying to do something, what we're not quite sure - you've made a classic XY problem mistake.. You have some task, like "implement auto login expiry if it's on in the prefs table" and you've devised this broken solution (use a computed column/IIF) and have sought help to know why it's broken.. It's not solving the actual core problem.
In transitioning from your current state to one where you're solving the problem, you can consider:
As a view:
CREATE VIEW yourtable_withexpiry AS
SELECT
*,
CASE WHEN [ExpiryDate] IS NULL OR config.[Value] = 1 AND SysDateTimeOffset() < [ExpiryDate] THEN 1 ELSE 0 END AS IsValid
FROM
yourtable
LEFT JOIN
config
ON config.property = 'AutomaticExpiry'
As a trigger:
CREATE TRIGGER trg_withexpiry ON yourtable
AFTER INSERT OR UPDATE
AS
IF NOT EXISTS(select * from Config where Property = 'AutomaticExpiry' and Value = 1)
RETURN;
UPDATE yourtable SET [ExpiryDate] = DATE_ADD(..some current time and suitable offset here..)
FROM yourtable y INNER JOIN inserted i ON y.pk = i.pk;
END;
But honestly, you should be doing this in your front end app. It should be responsible for reading/writing session data and keeping things up to date and kicking users out if they're over time etc.. Using the database for this is, to a large extent, putting business logic/decision processing into a system that shouldn't be concerned with it..
Have your front end language implement a code that looks up user info upon some regular event (like page navigation or other activity) and refreshes the expiry date as a consequence of the activity, only if the expiry date isn't passed. For sure too keep the thing valid if the expiry is set to null if you want a way to have people active forever (or whatever)

Select statement return if there is a value and if there is no value

Here's my code of the SQL Server stored procedure:
SELECT NOTES as DeletionNote
FROM STCRCHF_LOG
WHERE STHTR_ = #transferNo
IF ( ##ROWCOUNT = 0)
If there is data found, I just want to return the string of NOTES. Else if it doesn't have data, I just want to return an empty string or null.
Screenshot (executed stored procedure):
If there is data found. At my program on the web server side it gets the data.
If there is no data. In my program on the web server side it causes a NullReferenceException
If only a single record is possible then:
select coalesce((SELECT NOTES FROM STCRCHF_LOG
WHERE STHTR_ = #transferNo), '') as DeletionNote
If multiple records are possible then the following will ensure at least one row is returned:
SELECT NOTES as DeletionNote FROM STCRCHF_LOG WHERE STHTR_ = #transferNo
union all select '' /* or null if preferred */ where not exists (SELECT 1 FROM STCRCHF_LOG WHERE STHTR_ = #transferNo)
Another way which I like is to use a dummy value and OUTER APPLY like so.
-- sample data
DECLARE #table TABLE (someId INT IDENTITY, Col1 VARCHAR(100));
INSERT #table(Col1) VALUES ('record 1'),('record 2');
DECLARE #id INT = 11;
SELECT f.Col1
FROM (VALUES(NULL)) AS dummy(x)
OUTER APPLY
(
SELECT t.Col1
FROM #table AS t
WHERE t.someId = #id
) AS f;
Check If(DataTable.Rows.Count >0) check at your web programming level to avoid NullReferenceException. Based on the condition you can make the decision what to do with the further logic at web program.
It is always wise idea to handle such exception from programming level. Think of scenario where somebody else make changes in SQL query without knowing the effect of web usages of the code.

How do SQL Server variables work?

I have a table with salary of multiple employees.
I need to calculate score for these employees using a derived number (mean and stddev) and include it as a new column.
I know I can do this in the select statement, but if I wanted see if I can use variables.
So that changes can be made dynamically. I am new to SQL and just trying to learn things here.
declare #Weight int,
#weight2 int,
#Mean int,
#StdDevn int,
#Variance int
set #Weight = 30
set #weight2 = (POWER(#weight,2)/100)
SELECT #Mean = AVG(salary) FROM [live].[dbo].[salary]
SELECT #StdDevn = STDEV(salary) FROM [live].[dbo].[salary]
SELECT #Variance = (POWER(#StdDevn,2)/100)
-- I should include some code here so that the above variable are passed to below query
SELECT * FROM [live].[dbo].[salary], (([salary]-#Mean)/#StdDevn)
Error:
Must declare variable #Mean, Incorrect syntax near '-'.
The problem you have is how you are trying to select.
The variable #Mean is declared and it would work if the final select would be something like below:
SELECT *,
(([salary]-#Mean)/#StdDevn) as SomeOtherCol
FROM [live].[dbo].[salary]
The SomeOtherCol will be then shown as the last column of your result set.

SQL Server : converting varchar to INT

I am stuck on converting a varchar column UserID to INT. I know, please don't ask why this UserID column was not created as INT initially, long story.
So I tried this, but it doesn't work. and give me an error:
select CAST(userID AS int) from audit
Error:
Conversion failed when converting the varchar value
'1581............................................................................................................................' to data type int.
I did select len(userID) from audit and it returns 128 characters, which are not spaces.
I tried to detect ASCII characters for those trailing after the ID number and ASCII value = 0.
I have also tried LTRIM, RTRIM, and replace char(0) with '', but does not work.
The only way it works when I tell the fixed number of character like this below, but UserID is not always 4 characters.
select CAST(LEFT(userID, 4) AS int) from audit
You could try updating the table to get rid of these characters:
UPDATE dbo.[audit]
SET UserID = REPLACE(UserID, CHAR(0), '')
WHERE CHARINDEX(CHAR(0), UserID) > 0;
But then you'll also need to fix whatever is putting this bad data into the table in the first place. In the meantime perhaps try:
SELECT CONVERT(INT, REPLACE(UserID, CHAR(0), ''))
FROM dbo.[audit];
But that is not a long term solution. Fix the data (and the data type while you're at it). If you can't fix the data type immediately, then you can quickly find the culprit by adding a check constraint:
ALTER TABLE dbo.[audit]
ADD CONSTRAINT do_not_allow_stupid_data
CHECK (CHARINDEX(CHAR(0), UserID) = 0);
EDIT
Ok, so that is definitely a 4-digit integer followed by six instances of CHAR(0). And the workaround I posted definitely works for me:
DECLARE #foo TABLE(UserID VARCHAR(32));
INSERT #foo SELECT 0x31353831000000000000;
-- this succeeds:
SELECT CONVERT(INT, REPLACE(UserID, CHAR(0), '')) FROM #foo;
-- this fails:
SELECT CONVERT(INT, UserID) FROM #foo;
Please confirm that this code on its own (well, the first SELECT, anyway) works for you. If it does then the error you are getting is from a different non-numeric character in a different row (and if it doesn't then perhaps you have a build where a particular bug hasn't been fixed). To try and narrow it down you can take random values from the following query and then loop through the characters:
SELECT UserID, CONVERT(VARBINARY(32), UserID)
FROM dbo.[audit]
WHERE UserID LIKE '%[^0-9]%';
So take a random row, and then paste the output into a query like this:
DECLARE #x VARCHAR(32), #i INT;
SET #x = CONVERT(VARCHAR(32), 0x...); -- paste the value here
SET #i = 1;
WHILE #i <= LEN(#x)
BEGIN
PRINT RTRIM(#i) + ' = ' + RTRIM(ASCII(SUBSTRING(#x, #i, 1)))
SET #i = #i + 1;
END
This may take some trial and error before you encounter a row that fails for some other reason than CHAR(0) - since you can't really filter out the rows that contain CHAR(0) because they could contain CHAR(0) and CHAR(something else). For all we know you have values in the table like:
SELECT '15' + CHAR(9) + '23' + CHAR(0);
...which also can't be converted to an integer, whether you've replaced CHAR(0) or not.
I know you don't want to hear it, but I am really glad this is painful for people, because now they have more war stories to push back when people make very poor decisions about data types.
This question has got 91,000 views so perhaps many people are looking for a more generic solution to the issue in the title "error converting varchar to INT"
If you are on SQL Server 2012+ one way of handling this invalid data is to use TRY_CAST
SELECT TRY_CAST (userID AS INT)
FROM audit
On previous versions you could use
SELECT CASE
WHEN ISNUMERIC(RTRIM(userID) + '.0e0') = 1
AND LEN(userID) <= 11
THEN CAST(userID AS INT)
END
FROM audit
Both return NULL if the value cannot be cast.
In the specific case that you have in your question with known bad values I would use the following however.
CAST(REPLACE(userID COLLATE Latin1_General_Bin, CHAR(0),'') AS INT)
Trying to replace the null character is often problematic except if using a binary collation.
This is more for someone Searching for a result, than the original post-er. This worked for me...
declare #value varchar(max) = 'sad';
select sum(cast(iif(isnumeric(#value) = 1, #value, 0) as bigint));
returns 0
declare #value varchar(max) = '3';
select sum(cast(iif(isnumeric(#value) = 1, #value, 0) as bigint));
returns 3
I would try triming the number to see what you get:
select len(rtrim(ltrim(userid))) from audit
if that return the correct value then just do:
select convert(int, rtrim(ltrim(userid))) from audit
if that doesn't return the correct value then I would do a replace to remove the empty space:
select convert(int, replace(userid, char(0), '')) from audit
This is how I solved the problem in my case:
First of all I made sure the column I need to convert to integer doesn't contain any spaces:
update data set col1 = TRIM(col1)
I also checked whether the column only contains numeric digits.
You can check it by:
select * from data where col1 like '%[^0-9]%' order by col1
If any nonnumeric values are present, you can save them to another table and remove them from the table you are working on.
select * into nonnumeric_data from data where col1 like '%[^0-9]%'
delete from data where col1 like '%[^0-9]%'
Problems with my data were the cases above. So after fixing them, I created a bigint variable and set the values of the varchar column to the integer column I created.
alter table data add int_col1 bigint
update data set int_col1 = CAST(col1 AS VARCHAR)
This worked for me, hope you find it useful as well.

How to add column dynamically in where clause

I want to include column in where clause depending on the condition.
e.g
select * From emp
where id=7,
and if(code is not null) then code=8;
how can i do this in sql server
If I understand you correct, you could make use of COALESCE.
COALESCE()
Returns the first nonnull expression
among its arguments.
SQL Statement
SELECT *
FROM emp
WHERE id=7
AND code = COALESCE(#code, code)
If code is a column rather than a variable the query in your question would be rewritten as follows.
SELECT *
FROM emp
WHERE id=7 AND (code IS NULL OR code=8)
You'll probably have to create a query dynamically, as a string, and then use the Execute method to actually execute it. This approach has some potentially optimization issues, but it's commonly done. You might wan to Google T-SQL Dynamic Query, or something like that.
Also use this in case of Null value in #var1.
Select * from ABC where Column1 = isNull(#var1, Column1)
here is the example:
declare #SQL varchar(500)
declare #var1 int
set int = 1
set #SQL = 'Select * from ABC Where 1 = 1'
if(#var1 = 1)
set #SQL + #SQL ' And column1 = ' #var1
exec(#SQL)
You can use COALESCE function.
Well,
I don't know if i understood your question, but i guess that you want to include the value of the code column in the results.
If i'm right it can be done in the select part instead of the where clause. i. e.
Select ..., case when code is not null then 8 else code end as code from emp where id = 7
The other interpretation is that you want to filter rows where code <> 8,that would be
Select * from emp where id = 7 and (code is null OR code = 8)

Resources