Create Function SQL - sql

I need to create a function that would split string by element.
'a1,a2,a1,a3' and result should be:
Value
Counter
a1
2
a2
1
a3
1
This is example of my code (but it needs to work)
CREATE OR REPLACE FUNCTION SPLIT_STRING (
IN_STRING VARCHAR(1000),
IN_DELIM VARCHAR(20))
RETURNS TABLE (CNT INT)
LANGUAGE SQL
READS SQL DATA
BEGIN
DECLARE I INT DEFAULT 0;
DECLARE CNT INT DEFAULT 0;
WHILE I < LENGTH(IN_STRING)
DO
IF SUBSTR(IN_STRING, I, 1) = IN_DELIM THEN SET CNT = CNT + 1;
END IF;
SET I = I + 1;
END WHILE;
RETURN CNT;
END;
select SPLIT_STRING('a1,a2,a1,a3', ',') from SYSIBM.SYSDUMMY1

There is no need to create such a function in Db2 for LUW.
You may use a built-in functionality.
select x.tok as value, count (1) as counter
from
-- Here is your table reference
(values 'a1,a2,a1,a3') mytab (str)
, xmltable
(
'for $id in tokenize($s, ",") return <i>{string($id)}</i>'
passing
mytab.str as "s"
columns
tok varchar(4000) path '.'
) x
group by x.tok
VALUE
COUNTER
a1
2
a2
1
a3
1
Update
The same with a table function:
CREATE OR REPLACE FUNCTION SPLIT_STRING
(
IN_STRING VARCHAR(1000)
, IN_DELIM VARCHAR(20)
)
RETURNS TABLE (TOKEN VARCHAR (100), CNT INT)
LANGUAGE SQL
READS SQL DATA
RETURN
select x.tok as value, count (1) as counter
from xmltable
(
'for $id in tokenize($s, $p) return <i>{string($id)}</i>'
passing
IN_STRING as "s"
, IN_DELIM as "p"
columns
tok varchar (1000) path '.'
) x
group by x.tok
select *
from table (SPLIT_STRING ('a1,a2,a1,a3', ','))
TOKEN
CNT
a1
2
a2
1
a3
1
fiddle

Related

sort given string alphabetically and return letter for letter

I basically want to alphabetically sort a given string and output letter for letter via inline-function...
create function dbo.SortStringLetter4Letter (
#key varchar(64)
) returns table
as
return select ...
And I want to call up the function like this:
select *
from dbo.SortStringLetter4Letter('TEST');
go
And get a result like this:
id Sorted
----------- --------
1 E
2 S
3 T
4 T
Anybody got an idea for this problem?
This should do;
create or alter function dbo.SortStringLetter4Letter (
#key varchar(64)
) returns table
as
return
WITH Numbers AS
(
SELECT 1 AS Number
UNION ALL
SELECT Number+1
FROM Numbers
WHERE Number < Len(#key)
)
Select
id = Number, Sorted = SUBSTRING(#Key, Number, 1)
from Numbers
GO
Select * from dbo.SortStringLetter4Letter('test') order by sorted
This is the solution I worked out:
drop function if exists dbo.LetterForLetter;
create function dbo.LetterForLetter (
#key varchar(128)
) returns #chars table (# int, character char)
as
begin
insert into #chars
select #,
substring(#key, #, 1) "character"
from Numbers
where # <= len(#key)
and # > 0
return
end;
go
select *
from dbo.LetterForLetter('TEST');
go
The table Numbers is just filled with numbers btw. that I used to give the keys an ID

SQL replace with serial number for the specific occurrence of a character in a string

Input table has strings like this:
Col_Name
---------------
YXNYNXYYZY
YYZZY
-- and 100's of rows
I want to find specific occurrence of character Y, and create output field like this:
Col_Name
----------------
1,4,7,8,10
1,2,5
I am trying to find solution with sql functions like replace, len, charindex, etc,, but unable to arrive to the output. Please help.
This solution works but you should consider changing your model because this aproach may be slow and should not be SQL Server job's.
declare #search char(1) = 'Y'
; with input(string) as (
Select * From (values('YXNYNXYYZY'), ('YYZZY')) as input(string)
), find(id, string, pos) as ( => get 1 row each Y found and its position
select 0, string, CHARINDEX(#search, string, 0) From input
Where CHARINDEX(#search, string, 0) > 0
Union All
select id+1, string, CHARINDEX(#search, string, pos+1) From find
Where CHARINDEX(#search, string, pos+1) > 0
)
--Select * from find => 1 position per row
Select STUFF( --=> concatenate all position by string
(
Select ', ' + CAST([pos] AS Varchar(10))
From find f
Where (string = r.string)
Order By string, id
For XML PATH(''),TYPE
).value('(./text())[1]','Varchar(100)')
,1,2,'') AS x
From find r
Group BY string
with the help of function we can bring those values
alter FUNCTION [dbo].[GetPosition]
(
#txt varchar(max),
#Pat varchar(max)
)
RETURNS
#tab TABLE
(
ID int
)
AS
BEGIN
Declare #pos int
Declare #oldpos int
Select #oldpos=0
select #pos=patindex(#pat,#txt)
while #pos > 0 and #oldpos<>#pos
begin
insert into #tab Values (#pos)
Select #oldpos=#pos
select #pos=patindex(#pat,Substring(#txt,#pos + 1,len(#txt))) + #pos
end
RETURN
END
GO
Call Function
SELECT
stuff(
(
SELECT ','+ cast(ID as nvarchar(4)) FROM dbo.[GetPosition] ('YXNYNXYYZY','%Y%') FOR XML PATH('')
),1,1,'')
This would be trivial to do with a scripting language like php or python. Dump the entire dataset to an array and check each value. Event 100's of rows would not be an issue.
Load the CsV File
Search array for values.
//psuedo code (untested)
$Data = str_getcsv('c:\data\dumpeddata.csv');
foreach ($Data as $LineNo => $LineDetails) {
foreach($LineDetails as $ColNo => $ColData){
if (strpos($ColData , 'Y') !== false){
$Found .= "\nLine:$LineNo,Col:$ColNo";
}
}
}
echo $Found;
You'll be surprised how fast this runs.
declare #n int = (select charindex('Y',col_name,1) from tablename)
declare #l int = (select len(col_name) from tablename)
declare #res varchar(100) = ''
while #n <= #l and (select charindex('Y',col_name,#n) from tablename) <> 0
begin
set #res = #res + cast(select charindex('Y',col_name,#n) from tablename as varchar) + ' '
set #n = (select charindex('Y',col_name,#n) from tablename) + 1
end
select #res
This gives you an idea. If this has to be done repeatedly, it is better to wrap this in a function.

Change lower case to upper (title) case using sql query

i want to change case using sql query
e.g if text is : My nAme is iShAn halaRNkar (text is jumbled i.e it may contain Lower case or Upper case anywhere in the senetence)
than i want the output to be : My Name Is Ishan Halarnkar
i have not worked on sql queries much. Kindly help.
Here is another Microsoft SQL function:
CREATE FUNCTION PROPERCASE(#TEXT AS VARCHAR(MAX))
RETURNS VARCHAR(MAX)
AS
BEGIN
DECLARE #RESET BIT;
DECLARE #STR VARCHAR(MAX);
DECLARE #I INT;
DECLARE #C CHAR(1);
SELECT #RESET = 1, #I=1, #STR = '';
WHILE (#I <= LEN(#TEXT))
SELECT #C= SUBSTRING(#TEXT,#I,1),
#STR = #STR + CASE WHEN #RESET=1 THEN UPPER(#C) ELSE LOWER(#C) END,
#RESET = CASE WHEN #C LIKE '[A-ZA-Z]' THEN 0 ELSE 1 END,
#I = #I +1
RETURN #STR
END
There's no such function in any database which do this for you. You've to write a function which actually performs the check on each word in a sentence. Please check the solutions below:
MySql:
DELIMITER //
CREATE FUNCTION CAP_FIRST (input VARCHAR(255))
RETURNS VARCHAR(255)
DETERMINISTIC
BEGIN
DECLARE len INT;
DECLARE i INT;
SET len = CHAR_LENGTH(input);
SET input = LOWER(input);
SET i = 0;
WHILE (i < len) DO
IF (MID(input,i,1) = ' ' OR i = 0) THEN
IF (i < len) THEN
SET input = CONCAT(
LEFT(input,i),
UPPER(MID(input,i + 1,1)),
RIGHT(input,len - i - 1)
);
END IF;
END IF;
SET i = i + 1;
END WHILE;
RETURN input;
END//
DELIMITER ;
Example:
SELECT CAP_FIRST('this is exACtly tHe same!')
Output:
This Is Exactly The Same!
Copyrights:
http://joezack.com/2008/10/20/mysql-capitalize-function/
Hope this helps!
This SQL should work.
SELECT UPPER(LEFT(<ColumnName>, 1)) + LOWER(RIGHT(<ColumnName>,LEN(<ColumnName>)-1)) FROM {YourTableName}
First you need to create function
CREATE FUNCTION ProperCase(#OriginalText VARCHAR(8000))
RETURNS VARCHAR(8000)
BEGIN
DECLARE #CleanedText VARCHAR(8000)
;with
a1 as (select 1 as N union all select 1 union all
select 1 union all select 1 union all
select 1 union all select 1 union all
select 1 union all select 1 union all
select 1 union all select 1),
a2 as (select 1 as N from a1 as a cross join a1 as b),
a3 as (select 1 as N from a2 as a cross join a2 as b),
a4 as (select 1 as N from a3 as a cross join a2 as b),
Tally as (select top (len(#OriginalText)) row_number() over (order by N) as N from a4)
SELECT #CleanedText = ISNULL(#CleanedText,'') +
--first char is always capitalized?
CASE WHEN Tally.N = 1 THEN UPPER(SUBSTRING(#OriginalText,Tally.N,1))
WHEN SUBSTRING(#OriginalText,Tally.N -1,1) = ' ' THEN UPPER(SUBSTRING(#OriginalText,Tally.N,1))
ELSE LOWER(SUBSTRING(#OriginalText,Tally.N,1))
END
FROM Tally WHERE Tally.N
Now you just use this function
select dbo.ProperCase('My nAme is iShAn halaRNkar')

SQL function, extract numbers before -

i'm playing around with building a sql function that will extract numbers from a title, which is what the following code below does. Although, i want to modify this function to parse numbers into sections. For example:
Current Data in title field:
QW 1 RT 309-23-1
QW 1 RT 29-1
QW 1 RT 750-1
QW RT 750-1
Temp tables created once function is ran on title field:
column 1 Column 2 Column 3 Column 4
1 309 23 1
1 29 1 Null
1 750 1 Null
Null 750 1 Null
create function [dbo].[ExtractNumbers](#Numbers nvarchar(2000))
returns nvarchar(2000)
as
BEGIN
declare #NonNumericIndex int
set #NonNumericIndex = PATINDEX('%[^0-9]%',#Numbers)
WHILE #NonNumericIndex > 0
begin
SET #Numbers = REPLACE(#Numbers,SUBSTRING(#Numbers,#NonNumericIndex,1),'')
SET #NonNumericIndex = PATINDEX('%[^0-9]%',#Numbers)
SET
end
return #Numbers
END
Here's one way.
Although actually at the end I realised the format was more fixed than I had originally realised so you may be better off just using the various string manipulation functions to calculate the columns directly.
WITH TestTable AS
(
SELECT 'QW 1 RT 309-23-1' AS title UNION ALL
SELECT 'QW 1 RT 29-1' UNION ALL
SELECT 'QW 1 RT 750-1' UNION ALL
SELECT 'QW RT 750-1'
)
SELECT title, [1] AS [Column 1], [2] AS [Column 2],[3] AS [Column 3],[4] AS [Column 4]
FROM TestTable CROSS APPLY dbo.GetNumbers(title)
PIVOT
(MAX(num) FOR idx IN ([1], [2],[3],[4])
) AS PivotTable;
Uses the following TVF
CREATE FUNCTION GetNumbers
(
#Numbers NVARCHAR(2000)
)
RETURNS #Results TABLE
(
idx INT IDENTITY(1,1),
num INT
)
AS
BEGIN
DECLARE #NonNumericIndex INT, #NumericIndex INT
SET #NumericIndex = PATINDEX('%[0-9]%',#Numbers)
IF (#NumericIndex > 4) --First Column not there
INSERT INTO #Results VALUES (NULL)
WHILE #NumericIndex > 0
BEGIN
SET #Numbers = RIGHT(#Numbers,LEN(#Numbers)-#NumericIndex+1)
SET #NonNumericIndex = PATINDEX('%[^0-9]%',#Numbers)
IF(#NonNumericIndex = 0)
BEGIN
INSERT
INTO #Results VALUES (#Numbers)
RETURN
END
ELSE
INSERT
INTO #Results VALUES
(LEFT(#Numbers,#NonNumericIndex-1))
SET #Numbers = RIGHT(#Numbers,LEN(#Numbers)-#NonNumericIndex+1)
SET #NumericIndex = PATINDEX('%[0-9]%',#Numbers)
END
RETURN
END

SQL Server 2008 Splitting string variable number of token per line

I have a fairly simple requirement -I have a table with the following (relevant) structure.
with cte as(
select 1 id,'AA,AB,AC,AD' names union all
select 2,'BA,BB' union all
select 3,'CA,CB,CC,CD,CE' union all
select 4,'DA,DB,DC'
)
i would like to create a select statement which will split each "names" column into multiple rows.
For example the first row should produce
1,'AA'
1,'AB'
1,'AC'
1,'AD'
Can we do it using only SQL. This is failry easy to do in Oracle.
You can do it in one query with no custom defined functions if you leverage XML:
WITH cte AS( /*your data*/
SELECT 1 id,'AA,AB,AC,AD' names UNION ALL
SELECT 2,'BA,BB' UNION ALL
SELECT 3,'CA,CB,CC,CD,CE' UNION ALL
SELECT 4,'DA,DB,DC'
)
, xmlData AS ( /*make into xml*/
SELECT id, cast('<root><x>'+replace(names,',','</x><x>')+'</x></root>' as xml) AS theXML
FROM cte
)
SELECT id, x.value('.','varchar(100)') /*split up*/
FROM xmlData
CROSS APPLY xmlData.theXML.nodes('//x') AS func(x)
You can create a split function that returns a table, then select from that table.
/***************************************************************************
**
** Function: split
** In: #ipRowData - The delimited list of items to split.
** In: #ipSplitOn - The delimiter which separates the items in #rowData.
** Returns: A table object containing the split items. The table object
** will have an ID and Data column, where ID is the number of the item
** in the original list and Data is the value of the item.
**
** Description:
** Splits a delimited set of items and returns them
** as a table object.
***************************************************************************/
CREATE FUNCTION [dbo].[split]
(
#ipRowData NVARCHAR(4000),
#ipSplitOn NVARCHAR(5)
)
RETURNS #rtnValue table
(
ID INT identity(1,1),
Data NVARCHAR(100)
)
AS
BEGIN
DECLARE
#cnt INT
Set #cnt = 1
WHILE (Charindex(#ipSplitOn,#ipRowData)>0)
BEGIN
INSERT INTO #rtnValue
( data )
SELECT Data = ltrim(rtrim(Substring(#ipRowData,1,Charindex(#ipSplitOn,#ipRowData)-1)))
SET #ipRowData = Substring(#ipRowData,Charindex(#ipSplitOn,#ipRowData)+1,len(#ipRowData))
SET #cnt = #cnt + 1
END
INSERT INTO #rtnValue (data)
SELECT DATA = ltrim(rtrim(#ipRowData))
RETURN
END
GO
Sample Usage:
select 1,data from [dbo].split('AA,AB,AC,AD', ',');
Output:
(No column name) data
1 AA
1 AB
1 AC
1 AD