How to do Custom Sorting in SQL Server 2005 - sql

I want to do custom sort by Customercode for tblCustomer table.
CustomerCode consist of (3 char of Surname) + 1 + (PostCode)
Here, 1 will increment if Same Surname and postcode customer found.
For e.g. ABB12615, ABB22615
So mainly I want to sort this by
First 3 Letters of Surname + Index + PostCode.
I tried to do in this manner :
ORDER BY CHARINDEX(SUBSTRING(customerCode, 1, 3), customerCode)
but it gives me output like this:
ABB12615
ABB12715
...
...
...
..
.
ABB22615
But I want output in this order:
ABB12615
ABB22615
ABB12715
and so on
Is it possible to do?

Based on your expected results you really want to sort on
Surname, postcode, index
which would be
ORDER BY SUBSTRING(customerCode, 1, 3),
SUBSTRING(customerCode, 5, 4),
SUBSTRING(customerCode, 4, 1)

Try this
SELECT *
FROM TABLE1
ORDER BY CASE WHEN COlumn1 = 'ABB12615' THEN 1
WHEN COlumn1 = 'ABB22615' THEN 2
WHEN COlumn1 = 'ABB12715' THEN 3
END

This code should sort the way you want.
-- play table
create table #postal
(
id int identity(1,1) primary key,
code varchar(16)
)
go
-- remove data
truncate table #postal;
go
-- add data
insert into #postal
values
('ABB12615'),
('ABB22615'),
('ABB12715'),
('AAA29615'),
('AAA19615');
go
-- sort
select
*
from
#postal
order by
substring(code, 1, 3),
substring(code, 5, len(code) - 5),
substring(code, 4, 1)
Output from the test run.

Yes its possible.
Assuming that your CustomerCode format will remain the same, you can use the below code.
You need to split the Customercode based on String functions & before sorting index, need to convert them to integer as shown below:
select * from tblCustomer
ORDER BY
SUBSTRING(Customercode , 1, 3) --SurName
,CONVERT(INT, SUBSTRING(Customercode , 4, 1)) --Index
,CONVERT(INT,SUBSTRING(Customercode , 5, 5)) --Post Code; You can optionally remove the convert to int function if your post code will contain characters

Related

Conditional extraction of fixed width data using SQL

I have a scenario where I pull out data from multiple tables and the output is fixed width format. The fixed width output will look like:
Current output:
1001RJOHNKEITH25 20181017 NA
1002CDWANEKANE36 20181010 RR
1003CMIKAYLAGN44 20181011 RR
Desired output:
1001RJOHNKEITH25 20181017 NA
1002CDWANEKANE36 NA
1003RMIKAYLAGN44 20181010 RR
In this output, 1001 is the Person ID, R/C is the hard-coded indicator, then comes the name, age and registration date, record type. There is a condition for Registration date. If the record indicator is R, the registration date will show up. Otherwise, it should be null. I am not sure how to write a condition based on the fixed width field.
Rextester demo attached : https://rextester.com/MKESI50760
Any help?!
OK, well this is a little messy. But because your output is fixed width, you can always make the query into a view or a CTE (shown below) and then access specific positions in the string via SUBSTRING function.
There are LOT of drawbacks to doing this. If anybody changes the order or size of the fields being concatenated ... it all breaks. So, in the spirit of answering your question.. this is a way to do it. But I don't think It's a good way.
WITH BaseQuery as
(
select
t.Cid,
cast
(
concat(
LEFT(CONCAT(isnull(t.Cid,''),space(5)),5), -- PersonID
LEFT(CONCAT(isnull
((case when t.registeredonline = '1' and t.recordtype = 'NA' then 'R'
else 'C' end),''),space(10)),10),-- Record Indicator
LEFT(CONCAT(isnull(t.name,''),space(14)),14), --name
LEFT(CONCAT(isnull(t.age,''),space(5)),5), --age
LEFT(CONCAT(isnull(t.registrationdate,''),space(14)),14), -- Registration date should show up when record indicator is 'R'
LEFT(CONCAT(isnull(t.recordtype,''),space(3)),3) --Record type
) as nvarchar(max)
) result
from #temp t
)
SELECT
CONCAT(
SUBSTRING(result, 1, 34) -- portion before the 'registration date' region
, CASE WHEN SUBSTRING (RESULT, 6, 1) = 'R' THEN SUBSTRING (RESULT, 35, 10) ELSE SPACE(10) END
, SUBSTRING (RESULT, 46, 5)
)
FROM
BaseQuery
this gives the result:
1001 R JOHNKEITH 25 2018-10-17 NA
1002 C DWANEKANE 36 RR
1003 C JOHNKEITH 44 RR
The line
LEFT(CONCAT(isnull(t.registrationdate,''),space(14)),14)
become
CASE WHEN t.registeredonline = '1' and t.recordtype = 'NA' THEN LEFT(CONCAT(isnull(t.registrationdate,''),space(14)),14) ELSE SPACE(14) END, -- Registration date should show up when record indicator is 'R'
Just enclosing the original line with a condition to see if the result is 'R' or not.
The condition is showed up in the query from your link.
You just need to update one line in your query:
LEFT(CONCAT(isnull(t.registrationdate,''),space(14)),14), -- Registration date should show up when record indicator is 'R'
becomes
LEFT(CONCAT(isnull(CASE WHEN t.registeredonline = '1' and t.recordtype = 'NA' THEN CONVERT(char(10), t.registrationdate,126) ELSE NULL END,''),space(14)),14), -- Registration date should show up when record indicator is 'R'
This will check your date field and put in spaces instead of a date when the logic for record indicator evaluates to'R'
The 'convert' statement is needed otherwise the NULL date will end up showing as 1900-01-01.
Hope it helps.
Dealing with fixed width data:
Data in a fixed-width text file or string is arranged in rows and
columns, with one entry per row. Each column has a fixed width,
specified in characters, which determines the maximum amount of data
it can contain. No delimiters are used to separate the fields in the
file.
Parsing that data in T-SQL you can use SUBSTRING
https://learn.microsoft.com/en-us/sql/t-sql/functions/substring-transact-sql?view=sql-server-2017
SUBSTRING ( expression ,start , length )
Here's an example:
DECLARE #SampleData TABLE
(
[LineData] NVARCHAR(255)
);
INSERT INTO #SampleData (
[LineData]
)
VALUES ( '1001RJOHNKEITH25 20181017 NA' )
, ( '1002CDWANEKANE36 20181010 RR' )
, ( '1003CMIKAYLAGN44 20181011 RR' );
SELECT SUBSTRING([LineData], 1, 4) AS [PersonId]
, SUBSTRING([LineData], 5, 1) AS [Indicator]
, SUBSTRING([LineData], 6, 9) AS [Name]
, SUBSTRING([LineData], 15, 2) AS [Age]
, SUBSTRING([LineData], 18, 8) AS [RegDate]
, SUBSTRING([LineData], 27, 2) AS [RecordType]
, *
FROM #SampleData;
So in your example you're wanted to evaluate whether or not the "Indicator" is 'R', you can get to that value with:
SUBSTRING([LineData], 5, 1)
Not sure how that fits into what you have been tasked with. Based on other comments there's more to how this "Indicator" is determined.
Not ideal, but you could parse out all the fields and then put them back together doing the evaluation on that indicator field or use stuff in a case statement to replace the date with blanks when evaluating if indicator is R in the string.
DECLARE #SampleData TABLE
(
[LineData] NVARCHAR(255)
);
INSERT INTO #SampleData (
[LineData]
)
VALUES ( '1001RJOHNKEITH25 20181017 NA' )
, ( '1002CDWANEKANE36 20181010 RR' )
, ( '1003CMIKAYLAGN44 20181011 RR' );
--We check for R using substring
--when not equal to R we replace where Registration date in the string was with blanks.
SELECT CASE WHEN SUBSTRING([LineData], 5, 1) = 'R' THEN [LineData]
ELSE STUFF([LineData], 18, 8, ' ')
END AS [LineData]
FROM #SampleData;
Select ColA, CASE WHEN ColB (Criteria here) THEN NULL ELSE ColB END AS ColB, ColC

SQL: Extract last 5 digits in a string after special char

I am struggling to extract last 5 digits in title(free text field) after special char ': ' (with a space). Sample records are as follows:
title column
1 ABC Requirement1 - 1,500 - 3,000 sq m : 12345
2 10,000 sft shed requirement
3 OFFICES REQUIRED 500/700 SQ FT : 56789
4 Land Acquisition : 34567
5 Storage Requirement : 12345
6 Land Requirement :100 sq.m
my result set should be as follows:
ID
1 12345
3 56789
4 34567
5 12345
It should only pick up last 5 digits(ID) after special char ': ' and ignore other records with ': ' in between. I am trying to extract ID values to join with another table. Any help is highly appreciated!
This should get the query that you want.
SELECT LEFT(SUBSTRING(Title, CHARINDEX(': ', Title) + 2, LEN(Title)), 5)
FROM #table
WHERE [Title] LIKE '%: %'
AND ISNUMERIC(LEFT(SUBSTRING(Title, CHARINDEX(': ', Title) + 2, LEN(Title)), 5)) = 1
Try this query --
;WITH CTE
AS (
SELECT Id
,CASE
WHEN CHARINDEX(':', Title, 1) > 1
THEN SUBSTRING(Title, CHARINDEX(':', Title, 1) + 2, 5)
END AS TitleID
FROM RequirementTable
)
SELECT ID
,TitleID
FROM CTE
WHERE ISNUMERIC(TitleID) = 1;
First, you should seriously reconsider the way you're storing your data if you need to go to these lengths to form a relation between records. This is potentially disastrous should your data ever include ': ' naturally and without ending in a foreign key value. And you most likely won't figure that out until it's too late and processing and/or other applications fail as a result.
However, to answer the question as it was asked, I have the same thing as #ChesterLin, but with sample data and including the 'ID' column in the output.
DECLARE #Temp TABLE (ID int, Title varchar(255))
INSERT INTO #Temp
VALUES
(1, 'ABC Requirement1 - 1,500 - 3,000 sq m : 12345'),
(2, '10,000 sft shed requirement'),
(3, 'OFFICES REQUIRED 500/700 SQ FT : 56789'),
(4, 'Land Acquisition : 34567'),
(5, 'Storage Requirement : 12345'),
(6, 'Land Requirement :100 sq.m')
SELECT ID, LEFT(SUBSTRING(Title, CHARINDEX(': ', Title) + 2, LEN(Title)), 5) AS [Extracted Value]
FROM #Temp
WHERE [Title] LIKE '%: %'
AND ISNUMERIC(LEFT(SUBSTRING(Title, CHARINDEX(': ', Title) + 2, LEN(Title)), 5)) = 1
you can get last 5 digits
SUBSTR(column, LENGTH(column) - 5, 5)
OR
SELECT RIGHT('ABC Requirement1 - 1,500 - 3,000 sq m : 12345',5)
OR Full query
SELECT substr(title, character(title)-5) from table_name;
substr(column, -5, 5)
Starts from the last character in the string, and gives the five characters.
Then cast it as INT.
select cast(substr(column, -5, 5) as INT) as ID from table_name
where isnumeric(substr(column, -5, 5)) = 1
I hope this will work. Or, something like this.

SQL Query to parse numbers from name

The DBMS in this case is SQL Server 2012.
I need a SQL query that will grab just the numbers from a device name. I've got devices that follow a naming scheme that SHOULD look like this:
XXXnnnnn
or
XXXnnnnn-XX
Where X is a letter and n is a number which should be left padded with 0's where appropriate. However, not all of the names are properly padded in this way.
So, imagine you have a column that looks something like this:
Name
----
XXX01234
XXX222
XXX0390-A2
XXX00965-A1
I need an SQL query that will return results from this example column as follows.
Number
------
01234
00222
00390
00965
Anyone have any thoughts? I've tried things like casting the name first as a float and then as an int, but to be honest, I'm just not skilled enough with SQL yet to find the solution.
Any help is greatly appreciated!
SQL Server does not have great string parsing functions. For your particular example, I think a case statement might be the simplest approach:
select (case when number like '___[0-9][0-9][0-9][0-9][0-9]%'
then substring(number, 4, 5)
when number like '___[0-9][0-9][0-9][0-9]%'
then '0' + substring(number, 4, 4)
when number like '___[0-9][0-9][0-9]%'
then '00' + substring(number, 4)
when number like '___[0-9][0-9]%'
then '000' + substring(number, 4, 2)
when number like '___[0-9][0-9]%'
then '0000' + substring(number, 4, 1)
else '00000'
end) as EmbeddedNumber
This might work :
SELECT RIGHT('00000'
+ SUBSTRING(Col, 1, ISNULL(NULLIF((PATINDEX('%-%', Col)), 0) - 1, LEN(Col))), 5)
FROM (SELECT REPLACE(YourColumn, 'XXX', '') Col
FROM YourTable)t
SQLFIDDLE
This will work even when XXX can be of different len:
DECLARE #t TABLE ( n NVARCHAR(50) )
INSERT INTO #t
VALUES ( 'XXXXXXX01234' ),
( 'XX222' ),
( 'X0390-A2' ),
( 'XXXXXXX00965-A1' )
SELECT REPLICATE('0', 5 - LEN(n)) + n AS n
FROM ( SELECT SUBSTRING(n, PATINDEX('%[0-9]%', n),
CHARINDEX('-', n + '-') - PATINDEX('%[0-9]%', n)) AS n
FROM #t
) t
Output:
n
01234
00222
00390
00965
If the first 3 chars are always needed to be removed, then you can do something like that (will work if the characters will start only after '-' sign):
DECLARE #a AS TABLE ( a VARCHAR(100) );
INSERT INTO #a
VALUES
( 'XXX01234' ),
( 'XXX222' ),
( 'XXX0390-A2' ),
( 'XXX00965-A1' );
SELECT RIGHT('00000' + SUBSTRING(a, 4, CHARINDEX('-',a+'-')-4),5)
FROM #a
-- OUTPUT
01234
00222
00390
00965
Another option (will extract numbers after first 3 characters):
SELECT
RIGHT('00000' + LEFT(REPLACE(a, LEFT(a, 3), ''),
COALESCE(NULLIF(PATINDEX('%[^0-9]%',
REPLACE(a, LEFT(a, 3), '')),
0) - 1,
LEN(REPLACE(a, LEFT(a, 3), '')))), 5)
FROM
#a;
-- OUTPUT
01234
00222
00390
00965

SQL Server REPLACE AND CHECK IF EXISTS

I have to check the string with the following scenarios in WHERE condition.
The data ProductId stored in the database can be like
7314-3337 sometimes with - symbol and not prefixed with 19
73143337 sometimes without symbol and not prefixed with 19
1973143337 correct format
197314-3337 sometimes with - symbol
I need to filter the record ProductId and the input is correct format , i.e 1973143337
WHERE P.ProductId=#ProductId
How can i filter it if the data stored in other 3 formats?
How to use the string replace(-) and prefix 19 if not exists in SQL server?
please check this 2 approach.
one is very simple and second is some trick. (I think you go with second option which cover everythings)
declare #t table (ProductId varchar(100))
insert into #t
values
('7314-3337')
,('73143337')
,('1973143337')
,('197314-3337')
,('73683337')
,('73143338')
declare #valuetosearch varchar(100) = '1973143337'
--this is very simple , but not work in each schenerio. the second approach is fine.
--select CHARINDEX ( '19','1973143337'), SUBSTRING('1973143337',3,len('1973143337'))
--select * from
--#t
--where
--replace(REPLACE(ProductId ,'-','') ,'19','') = replace(REPLACE(#valuetosearch ,'-','') ,'19','')
select * from
#t
where
REPLACE( case when CHARINDEX ( '19',ProductId) = 1
then SUBSTRING( ProductId ,3,LEN(ProductId))
else ProductId
end ,'-','')
=
REPLACE ( case when CHARINDEX ( '19',#valuetosearch) = 1
then SUBSTRING( #valuetosearch ,3,LEN(#valuetosearch))
else #valuetosearch
end ,'-','')
You should first sanitize your data, if it is not consistent then you won't be able to get the correct results.
For prefixing with 19:
UPDATE foo
SET ProductId = '19' + ProductId
WHERE Left(ProductID, 2) <> '19'
For removing the '-':
UPDATE foo
SET ProductId = REPLACE(ProductId, '-', '')
Then you should be able to get the results you want.
UPDATE:
You could construct a CTE with the results in a single format, and then, filter that CTE:
WITH cte (
FormattedPID
,ProductId
)
AS (
SELECT CASE
WHEN LEFT(ProductId, 2) = '19'
THEN REPLACE(ProductId, '-', '')
ELSE '19' + REPLACE(ProductId, '-', '')
END
,ProductId
FROM foo
)
SELECT FormattedPID
,ProductId
FROM cte
WHERE FormattedPID = #ProductID
You could make sure the column is in the correct format like this:
Remove the - by replacing it with an empty string (197314-3337 -> 1973143337, 7314-3337 -> 73143337).
Add 19 at the beginning (1973143337 -> 191973143337, 73143337 -> 1973143337).
Take 10 rightmost characters of the result and compare to the input (1973143337 -> 1973143337, 1973143337 -> 1973143337).
In Transact-SQL:
WHERE RIGHT('19' + REPLACE(P.ProductId, '-', ''), 10) = #ProductId
Of course, this means no index seek for you, because we are applying functions to the column.
An alternative to that would be to produce the three non-standard formats out of the input:
cut off the initial 19 (1973143337 -> 73143337);
insert the - (1973143337 -> 197314-3337);
insert the - and cut off the 19 (1973143337 -> 197314-3337 -> 7314-3337).
In Transact-SQL:
WHERE P.ProductId IN (
#ProductId,
SUBSTRING(#ProductId, 3, 999999999),
STUFF(#ProductId, 7, 0, '-'),
SUBSTRING(STUFF(#ProductId, 7, 0, '-'), 3, 999999999)
)
This way if there is an index on P.ProductId, it will be used efficiently.
Both approaches assume that the length of the correct format is fixed.

Sql concatenate problem?

In my table i have a column called Dep_user_code which is nothing but employeeid...Everytime I need to increment the employeeid when i insert a new value..but it has both alphabet and number..i need to increment the number alone.. for example if my employeeid is 'NECUSER0001' means next time when i insert a new employeeid it has to be 'NECUSER0002' dynamically i have to generate like this..everytime when i insert a value it has to increment..
I have tried like taking the string part and number part like this but dont know how to implement this...Any suggestion?
select SUBSTRING(Dep_user_code,1,7) from NEC_Customer_User_Map
select SUBSTRING(Dep_user_code,8,4) from NEC_Customer_User_Map
you should also keep an identity key. use SELECT IDENT_CURRENT('NEC_Customer_User_Map') to find out last inserted ID.
If the value is always text then numbers, you split apart the value using Patindex:
Select Substring( Dep_user_code, 1, PatIndex( '%[0-9]%', Dep_user_code) - 1 ) As TextPortion
, Substring( Dep_user_code, PatIndex( '%[0-9]%', Dep_user_code)
, Len(Dep_user_code) ) As NumberPortion
However, whether you can use an identity in combination with a prefix depends on whether you can allow gaps. If you cannot allow gaps, then you need to query for the next id value that you can use which can be done in a variety of ways depending on the needs.
I've had to support databases with setups like this before and while I'm generally not a fan of this style, I'm assuming you have some reason for not storing the NECUSER in one column and the incrementing identity integer in another column with the PK set to both. If not, I'd suggest going that route and letting SQL do the work for you.
Otherwise, using the result of the following query should yield the results you want. I've added comments to try and answer any questions the query might raise.
SELECT SUBSTRING(Dep_user_code, 1, 7) +
RIGHT(
REPLICATE('0', 3) + --Ensure we have padding 0s
IsNull(MAX(CAST(SUBSTRING(Dep_user_code, 8, 4) AS INT), -1) + 1 --Work with ints, find MAX or set NULL to -1 so +1 will = 0
, 4) --Only want 4 character total from RIGHT function
FROM NEC_Customer_User_Map
WITH last AS (
SELECT MAX(Dep_user_code) AS Code
FROM NEC_Customer_User_Map
WHERE LEFT(Dep_user_code, 7) = 'NECUSER'
)
SELECT LEFT(Dep_user_code, 7) + RIGHT(CAST(STUFF(Code, 1, 7, '1') AS int) + 1, 4)
FROM last
The RIGHT part does the following:
replaces 'NECUSER' with '1' thus getting something like '10002';
casts the result as int;
increments by 1;
(implicitly) casts the value to varchar and gets the last 4 chars.
Maybe STUFF(Code, 1, 7, '1') should better be replaced with '1' + RIGHT(Code, 4), not sure.
EDIT: As it happens, the implicit conversion could also be employed in case of converting the string to the integer too:
... + RIGHT(STUFF(Code, 1, 7, '1') + 1, 4) ...
or
... + RIGHT('1' + RIGHT(Code, 4) + 1, 4) ...
declare #max varchar(20)
declare #number varchar(20)
select #max = max(cast(substring(dep_user_name , 8, 4) as int)) from NEC_Customer_User_Map (nolock)
select #max = isnull(#max, 0) + 1
select #max = (case when len(#max) = 1 then '000' + #max
when len(#max) = 2 then '00' + #max
when len(#max) = 3 then '0' + #max
else #max
end)
Select #number = (Substring( dep_user_name, 1, PatIndex( '%[0-9]%', dep_user_name) - 1 ) + #max) from NEC_Customer_User_Map
insert into NEC_Customer_User_Map(Dep_User_Name) values (#number )
You can consider to have both parts of Dep_user_code as separate fileds in your db in order to take advantage of several tsql features like IDENTITY and IDENT_CURRENT()