Removing varchar(s) after a semicolon - sql

So I have a lot of data like this:
pix11co;10.115.0.1
devapp087co;10.115.0.100
old_main-mgr;10.115.0.101
radius03co;10.115.0.110
And I want to delete the stuff after the ; so it just becomes
pix11co
devapp087co
old_main-mgr
radius03co
Since they're all different I can live with the semi-colon staying there.
I have the following query and it runs successfully but doesn't delete anything.
UPDATE dns$ SET [Name;] = REPLACE ([Name;], '%_;%__________%', '%_;');
What wildcards can I use to specify the characters after the ; ?

Can you use CHARINDEX? E.g.:
SELECT LEFT('pix11co;10.115.0.1', CHARINDEX(';', 'pix11co;10.115.0.1') - 1)

You can use SUBSTRING() and CHARINDEX() functions:
CREATE TABLE MyStrings (
STR VARCHAR(MAX)
);
INSERT INTO MyStrings VALUES
('pix11co;10.115.0.1'),
('devapp087co;10.115.0.100'),
('old_main-mgr;10.115.0.101'),
('radius03co;10.115.0.110');
SELECT STR, SUBSTRING(STR, 1, CHARINDEX(';', STR) -1 ) AS Result
FROM MyStrings;
Results:
+---------------------------+--------------+
| STR | Result |
+---------------------------+--------------+
| pix11co;10.115.0.1 | pix11co |
| devapp087co;10.115.0.100 | devapp087co |
| old_main-mgr;10.115.0.101 | old_main-mgr |
| radius03co;10.115.0.110 | radius03co |
+---------------------------+--------------+

Related

Removing all but one of a certain character in a string

I have an issue where I'm trying to remove all of the '.' from the string/filename below in SSMS apart from the last one which dictates file type.
EPC 14.10.14.pdf
Ideally I would like this string to appear as below:
EPC 141014.pdf
Any help would be appreciated
As a variable :
declare #doc varchar(30) = 'EPC 14.10.14.pdf'
declare #ext varchar(8) = right(#doc, charindex('.', reverse(#doc)));
set #doc = concat(replace(left(#doc,len(#doc)-len(#ext)),'.',''), #ext);
select #doc as doc;
doc
EPC 141014.pdf
As a table column :
create table test (
doc varchar(30) not null
);
insert into test (doc) values
('EPC 14.10.14.pdf'),
('FQD 15.11.15.jpeg');
select doc
, undotted_doc = concat(replace(left(doc, len(doc)-charindex('.', reverse(doc))),'.',''), right(doc, charindex('.', reverse(doc))))
from test;
doc
undotted_doc
EPC 14.10.14.pdf
EPC 141014.pdf
FQD 15.11.15.jpeg
FQD 151115.jpeg
Test on db<>fiddle here
Use replace,substring and len function
select replace(substring(#x,0,len(#x) - 3),'.','') + substring(#x,len(#x) - 3,len(#x))
EDIT:
If the name extension has a variable length, you can use the following query
select
CONCAT(
replace(substring(#x,0,len(#x) - CHARINDEX('.',TRIM(REVERSE(#x)))),'.','')
,
substring(#x,len(#x) - CHARINDEX('.',TRIM(REVERSE(#x))),len(#x))
)
Result
If you have extensions with different length (e.g. docx, xls), you need to find the index of the last occurrence of the . character using REVERSE() and CHARINDEX():
SELECT CONCAT(
REPLACE(SUBSTRING(SomeText, 1, LEN(SomeText) - CHARINDEX('.', REVERSE(SomeText))), '.', ''),
STUFF(SomeText, 1, LEN(SomeText) - CHARINDEX('.', REVERSE(SomeText)), '')
) AS FileName
FROM (VALUES
('EPC 14.10.14.pdf'),
('EPC 14.10.14.docx'),
('14.10.14.xlsx')
) t (SomeText)
Result:
FileName
----------------
EPC 141014.pdf
EPC 141014.docx
141014.xlsx
One more way.
SQL
SELECT fileName AS [Before]
, CONCAT(CONCAT(PARSENAME(fileName,4), PARSENAME(fileName,3), PARSENAME(fileName,2))
, '.', PARSENAME(fileName,1)) AS [After]
FROM (VALUES
('EPC 14.10.14.pdf'),
('EPC 14.10.14.docx'),
('14.10.14.xlsx'),
('csharp.10.14.cs')
) AS t(fileName);
Output
+-------------------+-----------------+
| Before | After |
+-------------------+-----------------+
| EPC 14.10.14.pdf | EPC 141014.pdf |
| EPC 14.10.14.docx | EPC 141014.docx |
| 14.10.14.xlsx | 141014.xlsx |
| csharp.10.14.cs | csharp1014.cs |
+-------------------+-----------------+

Find and replace by pattern

I have a table that has a column like the one below
url
----------------
dir=mp3\cat152AB&fileName=file-01.mp3
dir=mp3\cat2500DfDD00&fileName=file-02.mp3
dir=mp3\cat4500f0655&fileName=file-03.mp3
...
How can I delete extra strings and arrange the fields as follows in SQL Server.
url
----------------
file-01
file-02
file-03
...
you can use charindex and substring :
SELECT substring ('dir=mp3\cat152AB&fileName=file-01.mp3', CHARINDEX('fileName=', 'dir=mp3\cat152AB&fileName=file-01.mp3') +9 ,
LEN('dir=mp3\cat152AB&fileName=file-01.mp3')-CHARINDEX('fileName=', 'dir=mp3\cat152AB&fileName=file-01.mp3')
) AS MatchPosition;
CHARINDEX and SUBSTRING can help you, please check the example:
select substring (field, charindex (';fileName=', field) + len (';fileName='), len (field) - len ('.mp3') + 1 - charindex (';fileName=', field) - len (';fileName='))
from (
select 'dir=mp3\cat152AB&fileName=file-01.mp3' field union all
select 'dir=mp3\cat2500DfDD00&fileName=file-02.mp3' union all
select 'dir=mp3\cat4500f0655&fileName=file-03.mp3'
) a
The information you want always seems to be the 11th to 5th characters before the end of the string. I would suggest a simple solution:
select left(right(url, 11), 7)
Here is a db<>fiddle.
Please try the following method.
It is using tokenization via XML/XQuery.
SQL
-- DDL and sample data population, start
DECLARE #tbl TABLE (ID INT IDENTITY PRIMARY KEY, url VARCHAR(255));
INSERT INTO #tbl (url) VALUES
('dir=mp3\cat152AB&fileName=file-01.mp3'),
('dir=mp3\cat2500DfDD00&fileName=file-02.mp3'),
('dir=mp3\cat4500f0655&fileName=file-03.mp3');
-- DDL and sample data population, end
DECLARE #separator CHAR(1) = '=';
SELECT id, url
, LEFT(x, CHARINDEX('.', x) - 1) AS Result
FROM #tbl
CROSS APPLY (SELECT CAST('<root><r><![CDATA[' +
REPLACE(url, #separator, ']]></r><r><![CDATA[') +
']]></r></root>' AS XML)) AS t1(c)
CROSS APPLY (VALUES (c.value('(/root/r[last()]/text())[1]', 'VARCHAR(100)'))) AS t2(x);
Output
+----+------------------------------------------------+---------+
| id | url | Result |
+----+------------------------------------------------+---------+
| 1 | dir=mp3\cat152AB&fileName=file-01.mp3 | file-01 |
| 2 | dir=mp3\cat2500DfDD00&fileName=file-02.mp3 | file-02 |
| 3 | dir=mp3\cat4500f0655&fileName=file-03.mp3 | file-03 |
+----+------------------------------------------------+---------+
I know we have an accepted answer but I wanted to chime in with another simple, high-performing solution that addresses file names and file extensions with various lengths. For fun I included a parameter that allows you to include the file extension if you choose.
--==== Easily Consumable Sample Data
DECLARE #link TABLE ([url] VARCHAR(100) UNIQUE);
INSERT #link VALUES ('dir=mp3\cat152AB&fileName=file-01.mp3'),
('dir=mp3\cat2500DfDD00&fileName=file-02.mp3'),
('dir=mp3\cat4500f0655&fileName=file-03.mp3'),
('dir=mp3\cat4500f0655&fileName=file-999.mp3'),
('dir=mp3\cat4500d9997&fileName=file-0021.prodigi');
--==== Allows you to determine if you want the file extension
DECLARE #exclude BIT=1;
SELECT l.[url], TheFile = SUBSTRING(l.[url], s.Pos, s.Ln-s.Pos- ((#exclude*(fl.Ln)-1)))
FROM #link AS l
CROSS APPLY (VALUES(CHARINDEX('.',REVERSE(l.[url])))) AS fl(Ln)
CROSS APPLY (VALUES(CHARINDEX('fileName=',l.[url])+9, LEN(l.[url]))) AS s(Pos,Ln);
#exclude=1 returns:
url TheFile
----------------------------------------------------- --------------
dir=mp3\cat152AB&fileName=file-01.mp3 file-01
dir=mp3\cat2500DfDD00&fileName=file-02.mp3 file-02
dir=mp3\cat4500d9997&fileName=file-0021.prodigi file-0021
dir=mp3\cat4500f0655&fileName=file-03.mp3 file-03
dir=mp3\cat4500f0655&fileName=file-999.mp3 file-999
#exclude=0 returns:
url TheFile
----------------------------------------------------- --------------
dir=mp3\cat152AB&fileName=file-01.mp3 file-01.mp3
dir=mp3\cat2500DfDD00&fileName=file-02.mp3 file-02.mp3
dir=mp3\cat4500d9997&fileName=file-0021.prodigi file-0021.prodigi
dir=mp3\cat4500f0655&fileName=file-03.mp3 file-03.mp3
dir=mp3\cat4500f0655&fileName=file-999.mp3 file-999.mp3

SQLite- Normalizing a concatenated field and joining with it?

I have some data stored in comma-separated values in a field and I want to turn those comma-separated values into a temporary table and use those to join to another table
CREATE TABLE STRATEGY (STRATEGY_ID INTEGER PRIMARY KEY, APPLIED_SET_IDS VARCHAR);
CREATE TABLE ACTION_SET (APPLIED_ACTION_SET_ID INTEGER PRIMARY KEY, VALUE VARCHAR);
+-----------+---------------+
|STRATEGY_ID|APPLIED_SET_IDS|
+-----------+---------------+
| 1|1,3,6,7 |
| 2|1,2,4 |
+---------------------+-----+
|APPLIED_ACTION_SET_ID|VALUE|
+---------------------+-----+
| 1|X |
| 2|Y |
| 3|Z |
| 4|H |
| 5|I |
| 6|J |
| 7|K |
| 8|L |
I know I have to use some form of recursion as shown here. But every attempt I've done has made my head spin a bit. And my temporary table needs to preserve the original concatenated order of APPLIED_SET_ID values as well, like this...
+-----------+-----+--------------+
|STRATEGY_ID|ORDER|APPLIED_SET_ID|
+-----------+-----+--------------+
| 1| 1| 1|
| 1| 2| 3|
| 1| 3| 6|
| 1| 4| 7|
| 2| 1| 1|
| 2| 2| 2|
| 2| 3| 4|
Ultimately, I will join this table to the second existing table and use GROUP_CONCAT to replace the ID's with the corresponding values in the same order.
+-----------+------------------+
|STRATEGY_ID|APPLIED_SET_VALUES|
+-----------+------------------+
| 1|X,Z,J,K |
| 2|X,Y,H |
So regular expressions are out thanks to the order (otherwise I could have turned the commas to pipes and joined on a REGEXP statement). How can I achieve this? I know this is not normalized but I need to work with this current structure. Thank you for any help in advance.
It is possible to call PHP functions from SQLite. It makes it possible to use simple queries to 'normalize' the table.
I have converted the 'normalize comma delimited strings holding keys' to SQLite. see: (joining on ';' separated values in a column) for a more complete explanation
I started out looking for ways of converting the functions to run in SQLite. After some searching I came across this: Working with PHP UDFs in SQLite.
Which I found interesting - call PHP functions from SQLite. That sounds like fun!
It works but you cannot use PDO. You have to use the SQLite functions directly. Vendor Specific Database Extensions: SQLite3
Updated with your data (see previous edits for working code for other question)
The code:
<?php // Q34231542 -- count_in_set, value_in_set
/*
* See this question for a rather more complete explanation of what this is doing...
*
* https://stackoverflow.com/questions/33782728/can-i-resolve-this-with-pure-mysql-joining-on-separated-values-in-a-column/
*/
define('SQLITE_DB', __DIR__ .'/Q34231542.sqlite');
/**
* #var SQLite3
*/
$db = new SQLite3(SQLITE_DB);
/*
* Define the functions for use by SQLite.
*/
$db->createFunction('count_in_set', 'count_in_set', 2);
$db->createFunction('value_in_set', 'value_in_set', 3);
$sql ="
SELECT STRATEGY.STRATEGY_ID as 'applied_strategy_id',
STRATEGY.APPLIED_SET_IDS as 'applied_strategy_list',
isequence.id as 'which_strategy',
COUNT_IN_SET(STRATEGY.APPLIED_SET_IDS, ',') as 'StrategyCount',
VALUE_IN_SET(STRATEGY.APPLIED_SET_IDS, ',', isequence.id)
as 'TheStrategy',
ACTION_SET.VALUE as 'Action_Set_Value'
FROM STRATEGY
JOIN integerseries AS isequence
ON isequence.id <= COUNT_IN_SET(STRATEGY.APPLIED_SET_IDS, ',') /* normalize */
JOIN ACTION_SET
ON ACTION_SET.APPLIED_ACTION_SET_ID = VALUE_IN_SET(STRATEGY.APPLIED_SET_IDS, ',', isequence.id)
ORDER BY
STRATEGY.STRATEGY_ID , ACTION_SET.APPLIED_ACTION_SET_ID;
";
/*
* Run the query
*/
$stmt = $db->prepare($sql);
$result = $stmt->execute();
/*
* Get the results
*/
$rows = array();
while ($row = $result->fetchArray(SQLITE3_ASSOC)) { // fetch all the rows for now...
$rows[] = $row;
}
/*
* output...
*/
// \Kint::dump($rows);
echo '<pre>';
var_dump($rows);
echo '</pre>';
exit;
/* -------------------------------------------------------------------------
* The PHP functions called from SQLite
*/
/**
* Count the number of delimited items in a string
*
* #param string $delimitedValues
* #param string $delim
* #return integer
*/
function count_in_set($delimitedValues, $delim)
{
return substr_count(trim($delimitedValues, $delim), $delim) + 1;
}
/**
* Treat the delimited values as ONE BASED array.
*
* #param string $delimitedValues
* #param string $delim
* #param integer $which
* #return string
*/
function value_in_set($delimitedValues, $delim, $which)
{
$items = explode($delim, $delimitedValues);
return $items[$which - 1];
}
The output:
applied_strategy_id applied_strategy_list which_strategy StrategyCount TheStrategy Action_Set_Value
#1 1 "1,3,6,7" 1 4 "1" "X"
#2 1 "1,3,6,7" 2 4 "3" "Z"
#3 1 "1,3,6,7" 3 4 "6" "J"
#4 1 "1,3,6,7" 4 4 "7" "K"
#5 2 "1,2,4" 1 3 "1" "X"
#6 2 "1,2,4" 2 3 "2" "Y"
#7 2 "1,2,4" 3 3 "4" "H"
The data:
CREATE TABLE [integerseries] (
[id] INTEGER NOT NULL PRIMARY KEY);
INSERT INTO "integerseries" VALUES(1);
INSERT INTO "integerseries" VALUES(2);
INSERT INTO "integerseries" VALUES(3);
INSERT INTO "integerseries" VALUES(4);
INSERT INTO "integerseries" VALUES(5);
INSERT INTO "integerseries" VALUES(6);
INSERT INTO "integerseries" VALUES(7);
INSERT INTO "integerseries" VALUES(8);
INSERT INTO "integerseries" VALUES(9);
INSERT INTO "integerseries" VALUES(10);
CREATE TABLE STRATEGY (STRATEGY_ID INTEGER PRIMARY KEY, APPLIED_SET_IDS VARCHAR);
INSERT INTO "STRATEGY" VALUES(1,'1,3,6,7');
INSERT INTO "STRATEGY" VALUES(2,'1,2,4');
CREATE TABLE ACTION_SET (APPLIED_ACTION_SET_ID INTEGER PRIMARY KEY, VALUE VARCHAR);
INSERT INTO "ACTION_SET" VALUES(1,'X');
INSERT INTO "ACTION_SET" VALUES(2,'Y');
INSERT INTO "ACTION_SET" VALUES(3,'Z');
INSERT INTO "ACTION_SET" VALUES(4,'H');
INSERT INTO "ACTION_SET" VALUES(5,'I');
INSERT INTO "ACTION_SET" VALUES(6,'J');
INSERT INTO "ACTION_SET" VALUES(7,'K');
INSERT INTO "ACTION_SET" VALUES(8,'L');
My colleague developed a very clever solution, assuming the separator is a pipe | and not a comma ,.
He used REGEXP and the INSTR() function to get a numerical position, and that value drove the sorting.
SELECT STRATEGY_ID,
APPLIED_SET_IDS,
GROUP_CONCAT(VALUE,'|') as DESCRIPTION
FROM (
SELECT STRATEGY_ID,
APPLIED_SET_IDS,
CASE
WHEN APPLIED_ACTION_SET_ID = APPLIED_SET_IDS THEN 1
WHEN instr(APPLIED_SET_IDS, APPLIED_ACTION_SET_ID || '|') = 1 Then 1
WHEN instr(APPLIED_SET_IDS, '|' || APPLIED_ACTION_SET_ID || '|') > 0 Then instr(APPLIED_SET_IDS, '|' || APPLIED_ACTION_SET_ID || '|')
ELSE 999999
END AS APPLIED_ORDER,
VALUE
FROM STRATEGY
INNER JOIN ACTION_SET
ON ACTION_SET.APPLIED_ACTION_SET_ID REGEXP '^(' || STRATEGY.APPLIED_SET_IDS || ')$'
ORDER BY APPLIED_ORDER
) DESCRIPTIONS
GROUP BY 1,2
This gave me the exact output I was looking for.

Replace empty/null string with result from another record

I have a language table and want retrieve specific records for a selected language. However, when there is no translation present I want to get the translation of another language.
TRANSLATIONS
TAG LANG TEXT
"prog1" | 1 | "Programmeur"
"prog1" | 2 | "Programmer"
"prog1" | 3 | "Programista"
"prog2" | 1 | ""
"prog2" | 2 | "Category"
"prog2" | 3 | "Kategoria"
"prog3" | 1 | "Actie"
"prog3" | 2 | "Action"
"prog3" | 3 | "Dzialanie"
PROGDATA
ID | COL1 | COL2
1 | "data" | "data"
2 | "data" | "data"
3 | "data" | "data"
If I want translations from language 3 based on the ID's in table PROGDATA then I can do:
SELECT TEXT FROM TRANSLATIONS, PROGDATA
WHERE TRANSLATIONS.TAG="prog" & PROGDATA.ID
AND TRANSLATIONS.LANG=3
which would give me:
"Programista"
"Kategoria"
"Dzialanie"
In case of language 1 I get an empty string on the second record:
"Programmeur"
""
"Actie"
How can I replace the empty string with, for example, the translation of language 2?
"Programmeur"
"Category"
"Actie"
I tried nesting a new select query in an IIf() function but that obviously did not work.
SELECT
IIf(TEXT="",
(SELECT TEXT FROM TRANSLATIONS, PROGDATA
WHERE TRANSLATIONS.TAG="prog" & PROGDATA.ID
AND TRANSLATIONS.LANG=2),TEXT)
FROM TRANSLATIONS, PROGDATA
WHERE TRANSLATIONS.TAG="prog" & PROGDATA.ID
AND TRANSLATIONS.LANG=3
A SWITCH or CASE statement may work well. But try this:
SELECT
IIf(TEXT="",
(SELECT TEXT AS TEXT_OTHER FROM TRANSLATIONS, PROGDATA
WHERE TRANSLATIONS.TAG="prog" & PROGDATA.ID
AND TRANSLATIONS.LANG=2),TEXT) AS TEXT_FINAL
I am using TEXTOTHER and TEXTFINAL to reduce ambiguity in your field names. Sometimes this helps.
You may even need to apply the principle to the table name:
(SELECT TEXT AS TEXT_OTHER FROM TRANSLATIONS AS TRANSLATIONS_ALT...
Also, make sure your criterion is correct: an empty string, not a Null value.
IIf(TEXT="", ...
IIf(ISNULL(TEXT), ...
You can join TRANSLATIONS table again to get a "default" translation and use a CASE in the SELECT Statement.
SELECT
CASE
WHEN ISNULL(Translation.TEXT,"") = "" THEN DefaultLang.TEXT
ELSE Translation.Text
END
FROM TRANSLATIONS AS DefaultLang,TRANSLATIONS as Translation, PROGDATA
WHERE
DefaultLang.TAG="prog" & PROGDATA.ID AND Translation.TAG="prog" & PROGDATA.ID
AND DefaultLang.LANG=2
AND Translation.LANG=3
it is a pseudo-code idea...
I d try to add a checkEmpty function for each value returned. if is not empty, return the same.. if is empty return a new search from other languaje.
You need to cheak that the new value is not empty again of course.
create function checkEmpty( #word varchar(10), #languageNumber integer) returns varchar(10)
as
begin
declare #newWord
declare #newlanguage
if #word <> '' then return #word else
begin
//select new language
case languageNumber of
3 then #newlanguage = 1;
2 then #newlanguage = 3;
1 then #newlanguage = 2;
//search new lenguage
#newWord= SELECT TEXT FROM TRANSLATIONS, PROGDATA
WHERE TRANSLATIONS.TAG="prog" & PROGDATA.ID
AND TRANSLATIONS.LANG=#newlanguage
return #newWord
end;
end;
//FUNCTION CALL
SELECT dbo.checkEmpty(TEXT) FROM TRANSLATIONS, PROGDATA
WHERE TRANSLATIONS.TAG="prog" & PROGDATA.ID
AND TRANSLATIONS.LANG=3
I canabalized the solutions of #fossilcoder and #Smandoli and merged it in one solution:
SELECT
IIf (
NZ(TRANSLATION.Text,"") = "", DEFAULT.TEXT, TRANSLATION.TEXT)
FROM
TRANSLATIONS AS TRANSLATION,
TRANSLATIONS AS DEFAULT,
PROGDATA
WHERE
TRANSLATION.Tag="prog_" & PROGDATA.Id
AND
DEFAULT.Tag="prog" & PROGDATA.Id
AND
TRANSLATION.LanguageId=1
AND
DEFAULT.LanguageId=2
I never thought of referencing a table twice under a different alias

SQLite query for finding file path

I have a column in my SQLite database that contains a file path. Given a portion of the file path, I need to return all the next folders. I would also like to return whether the next portion is the last path in the string (or does not end in a '/'). So if I have the folders:
/my/folder/one
/my/folder/two
/my/folder/path/three
/another/path
/one/two/three
And I have the path:
/my/folder/
The result would return something along the lines of:
+----------+------+
| isLast | item |
+----------+------+
| 1 | one |
| 1 | two |
| 0 | path |
+----------+------+
I've been struggling with this for a while so if anyone can provide any guidance it would be greatly appreciated.
This query:
SELECT item NOT GLOB '*/*' AS isLast,
item
FROM (SELECT substr(MyPath, length('/my/folder/') + 1) AS item
FROM MyTable
WHERE MyPath GLOB '/my/folder/' || '*')
will give you a result like this:
isLast item
------ ----
1 one
1 two
0 path/three
Removing the subpath requires the instr() function, which was only recently introduced in SQLite 3.7.15:
SELECT isLast,
CASE WHEN isLast
THEN item
ELSE substr(item, 1, instr(item, '/') - 1)
END AS item
FROM (SELECT item NOT GLOB '*/*' AS isLast,
item
FROM (SELECT substr(MyPath, length('/my/folder/') + 1) AS item
FROM MyTable
WHERE MyPath GLOB '/my/folder/' || '*'))