I need to insert the numbers that fall between the Start and End column in the column "NumberList" (This should include both the Start and End numbers).
The query that I am using to get the above result is as follows
SELECT DISTINCT Number
, CONVERT(VARCHAR,Number) + REPLICATE('0',16-LEN(Number)) AS 'Start'
, CONVERT(VARCHAR,Number) + REPLICATE('9',16-LEN(Number)) AS 'End'
,NULL 'NumberList'
FROM Table
Could you please help me with the following as I would like the NumberList column to be a Primary Key too
Create function GetNumbersList (#intStart as bigint, #intEnd as bigint)
returns varchar(max)
as
begin
DECLARE #num INT = #intStart
DECLARE #numberlist as varchar(max)=''
WHILE(#num<=#intEnd)
begin
set #numberlist=#numberlist + cast(#num as varchar(20)) + N' '
SET #num = #num + 1
end
return(rtrim(#numberlist))
End
GO
select Number
, CONVERT(VARCHAR,Number) + REPLICATE('0',16-LEN(Number)) AS 'Start'
, CONVERT(VARCHAR,Number) + REPLICATE('9',16-LEN(Number)) AS 'End'
, dbo.GetNumbersList (CONVERT(VARCHAR,Number) + REPLICATE('0',16-LEN(Number)), CONVERT(VARCHAR,Number) + REPLICATE('9',16-LEN(Number))) as [NumbersList]
from Table
Related
I have a column (MarketID) in a table.
I have to derive a value out of it.
I have to check for occurrence of delimiter(.) in the second position and see if there are consecutive three numbers after the delimiter then get that value. If not check for occurrence of delimiter(.) in the fourth position and see if there are consecutive three numbers after the delimiter then get that value
else get 0.
1) In first record: '3.001.1.16', at the second position there is a delimiter(.) and consecutive 3 number exists (001), so my output would be 001..
2)In the second record '3.1.006.4.7',there is a delimiter at second position but we don't have three consecutive numbers so we check for the 4th position and there is a delimiter and consecutive three numbers exist so the output is 006 ..
3) no (.) delimiter so output=0.
create table dbo.SampleList
(
MarketID varchar(100)
)
insert into dbo.SampleList
select '3.001.1.16'
union all
select '3.1.006.4.7'
union all
select 'D16B000000:21109:4'
select * from dbo.SampleList
Assuming SQL Server from dbo, you could use a CASE statement:
SELECT MarketID,
CASE WHEN SUBSTRING(MarketID,2,1) = '.' AND TRY_CONVERT(int,SUBSTRING(MarketID,3,3)) IS NOT NULL THEN SUBSTRING(MarketID,3,3)
WHEN SUBSTRING(MarketID,4,1) = '.' AND TRY_CONVERT(int,SUBSTRING(MarketID,5,3)) IS NOT NULL THEN SUBSTRING(MarketID,5,3)
ELSE '0'
END
FROM #SampleList
TRY_CONVERT to int will verify that the 3 characters are numbers
Here's a solution using a function I've created a few years ago.
It allows you to split a string and get a table as a result.
CREATE FUNCTION [dbo].[splitStringToTable]
(
#List VARCHAR(MAX) ,
#Separator VARCHAR(MAX)
)
RETURNS #Results TABLE
(
ID INT
)
AS
BEGIN
SET #List = #List + ','
DECLARE #POS INT
DECLARE #TEMP VARCHAR(8000)
WHILE (Charindex(#Separator, #List)>0)
BEGIN
SET #POS = Charindex(#Separator, #List)
IF #POS > = 0
BEGIN
SET #TEMP = LEFT(#List, #POS-1)
IF #TEMP <> ''
INSERT INTO #Results (ID) VALUES (#TEMP)
SET #List = Substring(#List, Charindex(#Separator, #List)+len(#Separator), len(#List))
END
END
RETURN
END
GO
Usage:
SELECT *, ISNULL((SELECT TOP 1 ID FROM dbo.[splitStringToStringTable](MarketID, '.') WHERE LEN(ID) = 3), 0) AS Result
FROM SampleList
SELECT MarketID,
(CASE WHEN SUBSTRING(MarketID,2,1) = '.'
THEN
(CASE WHEN SUBSTRING(MarketID,6,1) = '.' THEN SUBSTRING (MarketID,3,3)
WHEN SUBSTRING(MarketID,4,1) = '.' THEN
(CASE WHEN SUBSTRING(MarketID ,8,1)='.' THEN SUBSTRING(MarketID,5,3) ELSE NULL END)ELSE NULL END)
WHEN MarketID NOT LIKE '%.%' THEN '0'
ELSE '0'
END ) AS Output
FROM dbo.SampleList
I have a VARCHAR column with data like this:
abc = :abc and this = :that
I need a query to find all of the special "words" that start with a colon in this column of data. I don't really need any other data (IDs or otherwise) and duplicates would be OK. I can remove duplicates in Excel later if need be. So if this was the only row, I'd like something like this as the output:
SpecialWords
:abc
:that
I'm thinking it'll require a CHARINDEX or something like that. But since there could be more than one special word in the column, I can't just find the first : and strip out the rest.
Any help is greatly appreciated! Thanks in advance!
You have to split this value based on spaces and return only fields that starts with a colon :, i provided 2 solutions to achieve this based on the result type you need (Table or Single Value)
Table-Valued Function
You can create a TV function to split this column into a table:
CREATE FUNCTION [dbo].[GETVALUES]
(
#DelimitedString varchar(8000)
)
RETURNS #tblArray TABLE
(
ElementID int IDENTITY(1,1), -- Array index
Element varchar(1000) -- Array element contents
)
AS
BEGIN
-- Local Variable Declarations
-- ---------------------------
DECLARE #Index smallint,
#Start smallint,
#DelSize smallint
SET #DelSize = 1
-- Loop through source string and add elements to destination table array
-- ----------------------------------------------------------------------
WHILE LEN(#DelimitedString) > 0
BEGIN
SET #Index = CHARINDEX(' ', #DelimitedString)
IF #Index = 0
BEGIN
IF ((LTRIM(RTRIM(#DelimitedString))) LIKE ':%')
INSERT INTO
#tblArray
(Element)
VALUES
(LTRIM(RTRIM(#DelimitedString)))
BREAK
END
ELSE
BEGIN
IF (LTRIM(RTRIM(SUBSTRING(#DelimitedString, 1,#Index - 1)))) LIKE ':%'
INSERT INTO
#tblArray
(Element)
VALUES
(LTRIM(RTRIM(SUBSTRING(#DelimitedString, 1,#Index - 1))))
SET #Start = #Index + #DelSize
SET #DelimitedString = SUBSTRING(#DelimitedString, #Start , LEN(#DelimitedString) - #Start + 1)
END
END
RETURN
END
And you can use it like the following:
DECLARE #SQLStr varchar(100)
SELECT #SQLStr = 'abc = :abc and this = :that and xyz = :asd'
SELECT
*
FROM
dbo.GETVALUES(#SQLStr)
Result:
Scalar-Valued Function
If you need to return a value (not table) so you can use this function which will return on all values separated by (line feed + carridge return CHAR(13) + CHAR(10))
CREATE FUNCTION dbo.GetValues2
(
#DelimitedString varchar(8000)
)
RETURNS varchar(8000)
AS
BEGIN
DECLARE #Index smallint,
#Start smallint,
#DelSize smallint,
#Result varchar(8000)
SET #DelSize = 1
SET #Result = ''
WHILE LEN(#DelimitedString) > 0
BEGIN
SET #Index = CHARINDEX(' ', #DelimitedString)
IF #Index = 0
BEGIN
if (LTRIM(RTRIM(#DelimitedString))) LIKE ':%'
SET #Result = #Result + char(13) + char(10) + (LTRIM(RTRIM(#DelimitedString)))
BREAK
END
ELSE
BEGIN
IF (LTRIM(RTRIM(SUBSTRING(#DelimitedString, 1,#Index - 1)))) LIKE ':%'
SET #Result = #Result + char(13) + char(10) + (LTRIM(RTRIM(SUBSTRING(#DelimitedString, 1,#Index - 1))))
SET #Start = #Index + #DelSize
SET #DelimitedString = SUBSTRING(#DelimitedString, #Start , LEN(#DelimitedString) - #Start + 1)
END
END
return #Result
END
GO
you can use it as the following
DECLARE #SQLStr varchar(100)
SELECT #SQLStr = 'abc = :abc and this = :that and xyz = :asd'
SELECT dbo.GetValues2(#SQLStr)
Result
in the table result line feed are not visible, just copy the data to an editor and it will appears as shown in the image
References
Splitting the string in sql server
One way is to write a specialized SPLIT function. I would suggest getting a TSQL Split function off the internet and see if you can adapt the code to your needs.
Working from scratch, you could write a function that loops over the column value using CHARINDEX until it doesn't find any more : characters.
How about using a charindex?
rextester sample:
create table mytable (testcolumn varchar(20))
insert into mytable values ('this = :that'),('yes'), (':no'), ('abc = :abc')
select right(testcolumn, charindex(':', reverse(testcolumn)) - 1) from mytable
where testcolumn like '%:%'
reference:
SQL Select everything after character
Update
Addressing Sami's:
Didn't see that two words could be in one colon, how about this?
select replace(substring(testcolumn, charindex(':', testcolumn), len(testcolumn)), ':', '')
Update again
I see, the actual statement is this = :that and that = :this
If performance is important then you want to use an inline table valued function to split the string and extract what you need. You could use delimitedSplit8K or delimitedSplit8K_lead for this.
declare #string varchar(8000) = 'abc = :abc and this = :that';
select item
from dbo.DelimitedSplit8K(#string, ' ')
where item like ':%';
returns:
item
------
:abc
:that
And for even better performance than what I posted above you could use ngrams8k like so:
declare #string varchar(8000) = 'abc = :abc and this = :that';
select position, item =
substring(#string, position,
isnull(nullif(charindex(' ',#string,position+1),0),8000)-position)
from dbo.ngrams8k(#string, 1)
where token = ':';
This even gives you the location of the item you are searching for:
position item
---------- -------
7 :abc
23 :that
Let me say upfront that I'm a brand-spanking-new SQL Developer. I've researched this and haven't been able to find the answer.
I'm working in SSMS 2012 and I have a one-column table (axis1) with values like this:
axis1
296.90, 309.4
296.32, 309.81
296.90
300.11, 309.81, 311, 313.89, 314.00, 314.01, V61.8, V62.3
I need to convert this column into multiple columns like so:
axis1 axis2 axis3 axis4
296.90 309.4 null null
296.32 309.81 null null
296.90 null null null
300.11 309.81 311 313.89...
So far I've tried/considered:
select case when charindex(',',Axis1,1)>0
then substring(Axis1,1,CHARINDEX(',',Axis1,1)-1)
else Axis1
end as Axis1
from tablex
That works fine for a known number of column values, but there could be 0, 1, or 20+ values in this column.
Is there any way to split an unknown quantity of comma-separated values that are in one column into multiple single-value columns?
Thanks in advance for any help everyone!
I made one assumption while creating this answer, which is that you need this as a separate stored proc.
Step 1
Create a data type to enable the use of passing a table-valued parameter (TVP) into a stored proc.
use db_name
GO
create type axisTable as table
(
axis1 varchar(max)
)
GO
Step 2
Create the procedure to parse out the values.
USE [db_name]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[usp_util_parse_out_axis]
(
#axis_tbl_prelim axisTable readonly
)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
declare #axis_tbl axisTable
--since TVP's are readonly, moving the data in the TVP to a local variable
--so that the update statement later on will work as expected
insert into #axis_tbl
select *
from #axis_tbl_prelim
declare #comma_cnt int
, #i int
, #sql_dyn nvarchar(max)
, #col_list nvarchar(max)
--dropping the global temp table if it already exists
if object_id('tempdb..##axis_unpvt') is not null
drop table ##axis_unpvt
create table ##axis_unpvt
(
axis_nbr varchar(25)
, row_num int
, axis_val varchar(max)
)
--getting the most commas
set #comma_cnt = (select max(len(a.axis1) - len(replace(a.axis1, ',', '')))
from #axis_tbl as a)
set #i = 1
while #i <= #comma_cnt + 1
begin --while loop
--insert the data into the "unpivot" table one parsed value at a time (all rows)
insert into ##axis_unpvt
select 'axis' + cast(#i as varchar(3))
, row_number() over (order by (select 100)) as row_num --making sure the data stays in the right row
, case when charindex(',', a.axis1, 0) = 0 and len(a.axis1) = 0 then NULL
when charindex(',', a.axis1, 0) = 0 and len(a.axis1) > 0 then a.axis1
when charindex(',', a.axis1, 0) > 0 then replace(left(a.axis1, charindex(',', a.axis1, 0)), ',', '')
else NULL
end as axis1
from #axis_tbl as a
--getting rid of the value that was just inserted from the source table
update a
set a.axis1 = case when charindex(',', a.axis1, 0) = 0 and len(a.axis1) > 0 then NULL
when charindex(',', a.axis1, 0) > 0 then rtrim(ltrim(right(a.axis1, (len(a.axis1) - charindex(',', a.axis1, 0)))))
else NULL
end
from #axis_tbl as a
where 1=1
and (charindex(',', a.axis1, 0) = 0 and len(a.axis1) > 0
or charindex(',', a.axis1, 0) > 0)
--incrementing toward terminating condition
set #i += 1
end --while loop
--getting list of what the columns will be after pivoting
set #col_list = (select stuff((select distinct ', ' + axis_nbr
from ##axis_unpvt as a
for xml path ('')),1,1,''))
--building the pivot statement
set #sql_dyn = '
select '
+ #col_list +
'
from ##axis_unpvt as a
pivot (max(a.axis_val)
for a.axis_nbr in ('
+ #col_list +
')) as p'
--executing the pivot statement
exec(#sql_dyn);
END
Step 3
Make a procedure call using the data type created in Step 1 as the parameter.
use db_name
go
declare #tvp as axisTable
insert into #tvp values ('296.90, 309.4')
insert into #tvp values ('296.32, 309.81')
insert into #tvp values ('296.90')
insert into #tvp values ('300.11, 309.81, 311, 313.89, 314.00, 314.01, V61.8, V62.3')
exec db_name.dbo.usp_util_parse_out_axis #tvp
Results from your example are as follows:
I came with a problem of sorting using ORDER BY. I found a lot of similar questions, but no answer fits my needs. The task is:
I have column [LABEL] which contains strings, and i want to get an order like this:
label
'1'
'2'
'11R'
'11T9'
'11T10'
'RT_5'
'RT_6'
'RT_10'
'RT_10b'
'RT_10dyn'
and so on...
instead of:
'1'
'11R'
'11T10'
'11T9'
'2S'
'RT_10'
'RT_10b'
'RT_10dyn'
'RT_5'
'RT_6'
the label columb might be like any combination of characters.
The problem is to find numbers in names, and if it is possible to sort by those numbers, then by other charaters...
After a few hours here is the solution:
I created a function to change the labels in specific way:
Each NUMBER in the input #in is replaced by the same number
writen in #digits chars WITH leadings zeros.
For example:
#digit = 4, #in = 'aa300bb' return = '_aa0300bb_'.
#digit = 5, #in = 'aa300bb' return = '_aa00300bb_'.
#digit = 3, #in = 'a2c4e5' return = '_a002c004e005_'.
And here is the function:
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[fnMixSort]')
AND type in (N'FN', N'IF', N'TF', N'FS', N'FT'))
DROP FUNCTION [dbo].[fnMixSort]
GO
CREATE FUNCTION [dbo].[fnMixSort] (
#in NVARCHAR(250),
#digits int
) RETURNS NVARCHAR(1000) AS
BEGIN
DECLARE
#starts int,
#i int, -- position where next NUMBER starts
#j int, -- position where next NUMBER ends
#temp nvarchar(1000)
set #starts = 1
set #in = '_' + #in + '_' -- extended LABEL: protection from EMPTY input
while (1=1)
begin
select #temp = substring(#in, #starts, len(#in))
-- #i #j - start/end position of first number
SELECT #i = COALESCE( PATINDEX('%[0-9]%',#temp ), 0)
SELECT #j = COALESCE( PATINDEX('%[0-9][^0-9]%',#temp ), 0)
if #i = 0 break -- no more NUMBERs in the LABEL
-- now we PUT at posiotion=#i+#start-1 specific numbers of '0'
select #in = STUFF(#in, #i + #starts - 1, 0, REPLICATE('0', #digits-#j+#i-1))
select #starts = #starts + #i + #digits - 1
end
-- -------- return ---------
RETURN #in
END
GO
lets create some table to check the function:
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[aaaa_test]')
AND type in (N'U'))
DROP TABLE [dbo].[aaaa_test]
GO
CREATE TABLE [dbo].[aaaa_test](
Label [varchar](255) NULL
)
INSERT INTO [dbo].[aaaa_test] ([Label])
VALUES ('bb'),('aa12'),(''),('30'),('10rt'),
('12ru'),('1rt'),('9rt'),('aa8'),('aa10'),('aa'),
('12rz'),('12rt'),('9rt5'),('9_rt_10_23'),('9_rt_10_5'),('9rt12'),
('12rz34'),('12rz3'),('12rz35c'),('12rz105b'),('12rt'),('9rt5'),('9rt10'),('9rt12')
select
[label]
,dbo.fnMixSort(Label,5) as [fnMixSort_returns]
from [dbo].[aaaa_test]
order by dbo.fnMixSort(Label,5)
And the result
label fnMixSort_returns
----------------------------------
1rt _00001rt_
9_rt_10_5 _00009_rt_00010_00005_
9_rt_10_23 _00009_rt_00010_00023_
9rt _00009rt_
9rt5 _00009rt00005_
9rt5 _00009rt00005_
9rt10 _00009rt00010_
9rt12 _00009rt00012_
9rt12 _00009rt00012_
10rt _00010rt_
12rt _00012rt_
12rt _00012rt_
12ru _00012ru_
12rz _00012rz_
12rz3 _00012rz00003_
12rz34 _00012rz00034_
12rz35c _00012rz00035c_
12rz105b _00012rz00105b_
30 _00030_
aa _aa_
aa8 _aa00008_
aa10 _aa00010_
aa12 _aa00012_
bb _bb_
it was my first time to post here...
hope it will help someone oneday..
You can substr [LABEL] column into different columns and then order by those columns. As null is sorted first you don't need to do anything extra for values with less character.
How ever you can also follow this thread here.
Here in this solution the logic is :-
If ID is numeric, add 21 '0's in front of the ID value and get the last 20 characters.
If ID is not numeric, add 21 ‘’s at the end of the ID value and get the first 20 characters.
Or this is a better solution for you query Sort Alphanumeric value
Let us see if it helps.
ANOTHER SOLUTION: different exchanged_label:
/** ==========================================================
FUNCTION DESCRIPTION
-------------------------------------------------------------
Function for special sorting - natural-mix sorting.
Order by : number in word are treated as number, not as a
characters only.
So 'a2' is before 'a10' and '9R' is before '10R' ...
-------------------------------------------------------------
Function puts special prefix before each number.
If number has 1 digit -> with prefix is 0A
If number has 2 digits -> with prefix is 0B
... ... ...
If number has 16 digits -> with prefix is 0P
If number has 17 digits -> with prefix is 0PA
If number has 18 digits -> with prefix is 0PB
... ... ...
If number has 32 digits -> with prefix is 0PP
If number has 33 digits -> with prefix is 0PPA
... and so on...
For example:
aa123bb9 -> aa0C123bb0A9
**/
CODE
CREATE FUNCTION [dbo].[fnMixSort] ( #in NVARCHAR(1000) ) RETURNS NVARCHAR(1000) AS
BEGIN
DECLARE
#starts int,
#i int, -- position where next NUMBER starts
#j int, -- position where next NUMBER ends
#temp nvarchar(1000)
set #starts = 1
set #in = '_' + #in + '_' -- extended LABEL: protection from EMPTY input
while (1=1)
begin
select #temp = substring(#in, #starts, len(#in))
SELECT #i = COALESCE( PATINDEX('%[0-9]%',#temp ), 0)
if #i = 0 break -- no more NUMBERs in the LABEL
SELECT #j = COALESCE( PATINDEX('%[0-9][^0-9]%',#temp ), 0)
select #temp = '0' -- numbers->must still be numbers: before letters
while (#j >= #i + 16)
begin
select #j = #j - 16
select #temp = #temp + 'P'
end
select #temp = #temp + CHAR(#j - #i + 65) -- char(65) is 'A'
select #in = STUFF(#in, #i + #starts - 1, 0, #temp)
select #starts = #starts + LEN(#temp) + (LEN(#temp)-2)*16 + #j
end -- while
RETURN #in
END
GO
results:
1rt _0A1rt_
9_rt_10_5 _0A9_rt_0B10_0A5_
9_rt_10_23 _0A9_rt_0B10_0B23_
9rt _0A9rt_
9rt5 _0A9rt0A5_
9rt5 _0A9rt0A5_
9rt10 _0A9rt0B10_
9rt12 _0A9rt0B12_
9rt12 _0A9rt0B12_
10rt _0B10rt_
12rt _0B12rt_
12rt _0B12rt_
12ru _0B12ru_
12rz _0B12rz_
12rz3 _0B12rz0A3_
12rz34 _0B12rz0B34_
12rz105b _0B12rz0C105b_
30 _0B30_
9234567890123456123456789012345rz38c _0PO9234567890123456123456789012345rz0B38c_
12345678901234561234567890123456rz35c _0PP12345678901234561234567890123456rz0B35c_
123456789012345612345678901234561rz36c _0PPA123456789012345612345678901234561rz0B36c_
aa _aa_
aa0A _aa0A0A_
aa0b _aa0A0b_
aa8 _aa0A8_
aa10 _aa0B10_
aa12 _aa0B12_
bb _bb_
Same approach as pi.314 but rewrite for PostgreSQL:
CREATE OR REPLACE FUNCTION fnNumberAwareSort(value varchar, digits integer)
RETURNS varchar
AS '
DECLARE
numbers VARCHAR[];
texts VARCHAR[];
BEGIN
value = CONCAT(''_'', value, ''_'');
SELECT ARRAY(SELECT res[1] FROM regexp_matches(value, ''\d+'', ''g'') AS res) INTO numbers;
texts = regexp_split_to_array(value, ''\d+'');
FOR i IN 1..array_upper(texts,1) LOOP
numbers[i] = lpad(numbers[i], digits, ''0'');
END LOOP;
value = texts[1];
FOR i IN 2..array_upper(texts,1) LOOP
value = value || numbers[i-1] || texts[i];
END LOOP;
RETURN value;
END;
' LANGUAGE plpgsql;
I want to generate a pattern in SQL Server.
e.g. PA0001
and increment it every time (meaning if PA0001 has already been generated the next should be PA0002 and so on).
I have no idea how to go about this!
Can anyone help?
The canonical way is to have an indentity column and then create this code as a computed column:
create table . . . (
inc int not null identity(1, 1),
. . .
code as ('PA' + right('0000' + cast(inc as varchar(255)), 4)
This only works with one prefix -- if you want all values to start with 0001. If you have multiple prefixes, you can:
Use a trigger.
Calculate the code in queries.
Change your requirements so you can use codes that do not have other meanings.
You can write as:
create table Test
(
ID int identity not null primary key,
pattern varchar(100)
)
Go
create function Nextpattern (#id int)
returns varchar(20)
as
begin
return 'PA' +
CONVERT(VARCHAR(10), GETDATE(), 110) + right('00' + convert(varchar(10), #id), 2)
end
--Solution 1:
Go
alter table Test add Newpattern as dbo.Nextpattern(ID)
Go
insert into Test values (1)
Go
--solution2:
-- not good as what if two processes attempt to
-- add a row to the table at the exact same time?
create function dbo.fnNextpattern()
returns varchar(20)
as
begin
declare #lastval varchar(20)
set #lastval = (select max(pattern) from Test)
if #lastval is null set #lastval = 'PA' +
CONVERT(VARCHAR(10), GETDATE(), 110) + '01'
declare #i int
set #i = right(#lastval,2) + 1
return 'PA' +
CONVERT(VARCHAR(10), GETDATE(), 110)+ right('00' + convert(varchar(10),#i),2)
end
go
DEMO