Update Unique Column partial string SQL - sql

I have a table that has a user names in the column. This is an example
| **Path**
| /test/path/Barry/home
| /test/path/Jenny/home
| /test/path/Jermehiam/home/Docs
| /test/path/Sarah/home/Docs
I am not sure how to update just the part of the path that ends at 'home'. I need the other parts of the path to remain as the string I am replacing is with an environment variable. So the end result would be:
| **Path**
| ${PATH}
| ${PATH}
| ${PATH}/Docs
| ${PATH}/Docs
Any help would be appreciated

You may do it in this way:
UPDATE PathTable
SET Path = '...'
WHERE Path LIKE '/test/path/%/home'
UPDATE PathTable
SET Path = '.../Docs'
WHERE Path LIKE '/test/path/%/home/Docs'

if you are using SQL-Server you can use CHARINDEX(), SUBSTRING() and LEN()
SELECT CASE WHEN CHARINDEX('home',url) = 0
THEN url
ELSE '${PATH}' + SUBSTRING(url, (CHARINDEX('home',url) + 4), LEN(url) - (CHARINDEX('home',url) + 3))
END url
FROM t_link
in Oracle, you can use INSTR(), SUBSTR() and LENGTH()
SELECT CASE WHEN CHARINDEX('home',url) = 0
THEN url
ELSE '${PATH}' + SUBSTRING(url, (CHARINDEX('home',url) + 4), LEN(url) - (CHARINDEX('home',url) + 3))
END url
FROM t_link
Result
url
**Path**
${PATH}
${PATH}
${PATH}/Docs
${PATH}/Docs

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 |
+-------------------+-----------------+

Rails sanitize user input in active record query

Let's say I have following data:
models/supplier.rb
| -- | ---------------- |
| id | name |
| -- | ---------------- |
| 1 | John Doe's Store |
| 2 | Jane |
| -- | ---------------- |
I have following queries which are failing to sanitize user's input from search field:
#term = "John Doe's"
Query 1
Supplier.order("case when name LIKE :term 1 else 2 end, name asc", term: "#{#term}%")
ArgumentError: Direction "one's%" is invalid. Valid directions are: [:asc, :desc, :ASC, :DESC, "asc", "desc", "ASC", "DESC"]
from ~/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/activerecord-4.2.7/lib/active_record/relation/query_methods.rb:1113:in `block (2 levels) in validate_order_args'
Query 2
Vulnerable to SQL Injection attack
Supplier.order("case when name LIKE '#{#term}%' then 1 else 2 end, name ASC").first
Supplier Load (2.6ms) SELECT "suppliers".* FROM "suppliers" ORDER BY case when name LIKE 'John Doe's%' then 1 else 2 end LIMIT 1
ActiveRecord::StatementInvalid: PG::SyntaxError: ERROR: syntax error at or near "s"
LINE 1: ...uppliers" ORDER BY case when name LIKE 'John Doe's%' then 1..
Query 3
It will success for normal inputs which don't have ' (special character) in them, but this query is still vulnerable to SQL injection attack
#term = "John"
Supplier.order("case when name LIKE '#{#term}%' then 1 else 2 end, name ASC").first
#<Supplier:0x007fe4bfd8d758
id: 188,
name: "John Doe's Store">
I am not able to figure out a solution for this problem, Please help me complete this query in secure way.
All you need to escape your input is to use
ActiveRecord::Base.connection.quote(value)
This works for all types and is what rails use.
#term = ActiveRecord::Base.connection.quote("John Doe's" + "%" )
Supplier.order("case when name LIKE #{#term} then 1 else 2 end, name ASC").first
You can use the base connection quote which will sanitize the input
#term = "John Doe's"
like_value = ActiveRecord::Base.connection.quote(#term + '%')
Supplier.order("case when name LIKE #{like_value} 1 else 2 end, name asc")
Read about it here...
http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/Quoting.html#method-i-quote
Rails offers sanitize_sql_like() for this.
So your example would look like this:
#term = "John Doe's"
like_value = sanitize_sql_like(#term + '%')
Supplier.order("case when name LIKE #{like_value} 1 else 2 end, name asc")
See:
https://api.rubyonrails.org/classes/ActiveRecord/Sanitization/ClassMethods.html#method-i-sanitize_sql_like
EDIT:
If you need to use it in a Controller, call it like this:
ActiveRecord::Base::sanitize_sql_like()

Removing varchar(s) after a semicolon

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 |
+---------------------------+--------------+

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/' || '*'))