Take substring and use it in multiple where condition in sql - mysql

I have a string which has some values concatenated. Like 'Val1val2val3val4'.
I have a query like
Select * from table where var1 = Val1 and var2 = val2 and var3 = var3;
Val1 and val2 are length only 4 but val3 may differ in length. Val4 is of length 8 and no use.
I can form query with functions like var1 = substring (text, 1, 4), var2 = substring (text, 5, 8) and for var3 = substring (text, 9, Len(text) - 8).
But the problem is, i have to manually edit replace the value in 3 places in query each time in workbench. Which is a painful task in my case. Is it possible to just put the string in one place and make the SQL automatically take substrings and use in where clause conditions? I have only read access, cannot modify tables. Local variables like #var = Text, throws error code 1046. Hands tied for me. Need ideas if feasible.

You could put the input string into a derived table of one row, and cross-join that to your table:
SELECT ...
FROM (SELECT 'Val1val2val3val4' AS text) AS v
CROSS JOIN MyTable
WHERE var1 = substring(v.text, 1, 4) AND ...
Or you could use a user-defined variable and use it in a subsequent query:
SET #var = 'Val1val2val3val4';
SELECT ...
FROM MyTable
WHERE var1 = substring(#var, 1, 4) AND ...
You mentioned you got a 1046 error, but it's not clear how you were trying to assign it, so I can't guess what happened.

If you are sure that both #var1 and #var2 have length of 4 chars, then all you need is:
WHERE text LIKE CONCAT(#var1, #var2, #var3, REPEAT('_', 8))
If not then:
WHERE text LIKE CONCAT(#var1, #var2, #var3, REPEAT('_', 8))
AND CHAR_LENGTH(#var1) = 4
AND CHAR_LENGTH(#var2) = 4;
If the comparison should be case sensitive, change to:
WHERE text LIKE BINARY CONCAT(#var1, #var2, #var3, REPEAT('_', 8))

Related

MySQL, Concat int and save to veriable and use variable with 'IN' or 'NOT IN'

I want to CONCAT value in ID column in string variable and use the variable with IN in SQL as under:
SET #ActID = CONCAT(CAST(5 AS CHAR),',',CAST(15 AS CHAR));
SELECT * FROM `accounts` WHERE `ID` IN (#ActID);
It returns record having ID = 5 and ignore record having ID = 15.
#ActID is a comma separated list string literal and not a list of values.
So the list inside the parentheses of the IN operator contains only 1 value: '5,15'
when you compare 5 to '5,15' the result is TRUE
when you compare 15 to '5,15' the result is FALSE
because '5,15' is converted to the integer value 5 according to the rules described here.
What you want is the function FIND_IN_SET():
SET #ActID = CONCAT(CAST(5 AS CHAR),',',CAST(15 AS CHAR));
SELECT * FROM `accounts` WHERE FIND_IN_SET(`ID`,#ActID) > 0;
Change to ...= 0 for the equivalent of NOT IN.
See a simplified demo.
Note: SET #ActID = CONCAT(5,',',15); works fine.

Removing certain strings in an Access 2010 query

I currently have an Access query that returns data in two forms
One form is 5 numbers, e.g., 12345
The other form is 2 letters, followed by 5 numbers, e.g., ab12345
This is coming in the same field, and I need to get only the numbers. I can't strip off the first two characters, because then for the IDs that have only the numbers, I'm cutting it short.
Use Val(string) to strip letters after numbers.
Use pVal(string) to strip letters before numbers.
string1 = "123456abcd1234"
val1 = val(string1) => val1 = 123456
string2 = "aqweqweasd123456abcd1234"
val2 = pval(string2) => val2 = 123456
if in case you have "asdasd123asasd456" and you want to have 123456 as result you can modify the pval to replace all non numeric values from output
here is the pVal function. Place this in a module and use it from anywhere
Public Function pVal(s As String)
Dim i As Long
For i = 1 To Len(s)
If IsNumeric(Mid(s, i, 1)) Then
pVal = Val(Mid(s, i, Len(s)))
Exit Function
End If
Next i
End Function
Use a select query as source for your form like this with an expression that strips the two leading characters:
Select *, Mid([XX0000NumberField], 3) As NumberOnlyID
From YourTable
Or for mixed numbers:
Select *, Right([XX0000NumberField], 5) As NumberOnlyID
From YourTable

Find an array values in another array

I have 2 strings that are not ordered for example orange is not always at second place in first line or other values.
apple, orange, water
water, juice, orange, something, apple
I want TRUE if all first line values exist in second line.
I've tried REGEXP 'apple|orange|water' but | is or not and that it gives me true if one of them exists there not all of them unless I write every possible sort.
I've also tried IN() but it needs 3 statements like :
... 'apple' IN('water', 'juice', 'orange', 'something', 'apple') AND
'orange' IN('water', 'juice', 'orange', 'something', 'apple') AND
'water' IN('water', 'juice', 'orange', 'something', 'apple') ...
Also tried LIKE but it should be like IN() in making queries.
I tried Match() Against() after all, but it doesn't work in join statement. look at this:
SELECT *
FROM t1
INNER JOIN t2
ON
t1.sth = t2.sth AND
MATCH(t1.sthelse) AGAINST(t2.sthelse IN BOOLEAN MODE)
There's 2 problems here. First, it doesn't work in join (also used where but second problem not solved. Second, AGAINST should be string :-? With Concat() I couldn't do the trick the error exists.
I want to do it not in these hard ways and also for my purpose, I should do it with mysql and cannot access php for manipulating these data.
+ I can change split character to anything.
Any idea... Appreciated.
My linux VM is still loading so the following code is tested in MS SQL. The pattern matching may break. For instance, your first string is "apple, orange, water" and your second string is "apple pie, orange pie, water bottle".
This is a good reference for converting string to table variable.
How to split string and insert values into table in SQL Server
DECLARE #FirstLineValues varchar(max), #SecondLineValues varchar(max)
DECLARE #eachValue VARCHAR(20)
SELECT #FirstLineValues = 'abc,3,3,5,6,3', #SecondLineValues = 'abc,2,3,4,5,6,7,8,9'
DECLARE #tblFirstLineValues TABLE(Value varchar(10))
DECLARE #tblSecondLineValues Table (value varchar(10))
-------------------------------------------------------------------------
DECLARE #ind int, #allExist bit
SET #ind = CHARINDEX(',',#FirstLineValues)
WHILE #ind > 0
BEGIN
SET #eachValue = SUBSTRING(#FirstLineValues, 1, #ind-1)
SET #FirstLineValues = SUBSTRING(#FirstLineValues, #ind+1, LEN(#FirstLineValues)-#ind)
INSERT INTO #tblFirstLineValues values (#eachValue)
SET #ind = CharIndex(',', #FirstLineValues)
END
SET #eachValue = #FirstLineValues
INSERT INTO #tblFirstLineValues values (#eachValue)
-------------------------------------------------------------------------
SET #ind = CHARINDEX(',',#SecondLineValues)
WHILE #ind > 0
BEGIN
SET #eachValue = SUBSTRING(#SecondLineValues,1,#ind-1)
SET #SecondLineValues = SUBSTRING(#SecondLineValues,#ind+1,LEN(#SecondLineValues)-#ind)
INSERT INTO #tblSecondLineValues values (#eachValue)
SET #ind = CharIndex(',',#SecondLineValues)
END
SET #eachValue = #SecondLineValues
INSERT INTO #tblSecondLineValues values (#eachValue)
-------------------------------------------------------------------------
SELECT #allExist = IIF(count(*) = 0, 1, 0)
FROM #tblFirstLineValues flv
LEFT OUTER JOIN #tblSecondLineValues slv ON flv.value = slv.value
WHERE slv.value IS NULL
-------------------------------------------------------------------------
select #allExist 'All first line values exist in second line'

Separate string into columns

Data :
Tree Depth
URL1||URL2 2
URL2||URL3 2
URL3||URL4||URL5 3
URL1||URL2||URL3 3
In the above data the Tree column consists of string separated by "||". I need to convert the above data such that I have 3 columns (since the max depth is 3 in this example) , the result should look like:
COL1 COL2 COL3 DEPTH
URL1 URL2 2
URL2 URL3 2
URL3 URL4 URL5 3
URL1 URL2 URL3 3
In the above example the max depth is 3 however in real world it could be N number.
Good day,
In first glance it is look like we need to use user defined SPLIT function but since number of values that you have in each string is not more then 4, there is a much simpler and probably much better solution. We just need to use the built-in PARSENAME function.
I did not test the code but the solution should be something like this:
SELECT PARSENAME(REPLACE(Tree,'||','.'), 1) as col1, PARSENAME(REPLACE(Tree,'||','.'), 2) as col2, PARSENAME(REPLACE(Tree,'||','.'), 3) as col3, Depth
from TableName
I replace the || with dot, since PARSENAME parse names that split by dot. this is the trick :-)
I actually mentioned example like this in my lecture at the sqlsaturday #360. You can see the presentation. The lecture was about WHY to use SQLCLR, and not less important WHEN to use it over transact-SQL. but I also talked about when NOT to use it, and this was one of the examples there.
In any case! if you are going to use SPLIT function then you should use SQLCLR and not T-SQL, as you can see here.
Try this, you just need to enter your Input Table, Output Table, Delimeter and Column to split. It can handle depth of more than 3, unlike PARSENAME function.
It is tested with 100,000 records and 30 split columns. It takes 10 sec to create the desired output.
Declare #Delimiter nvarchar(10) = '||'
Declare #InputTable nvarchar(2000) = '<<input table name>>'
Declare #OutputTable nvarchar(2000) = '<<output table name>>'
Declare #ColumnToSplit nvarchar(2000) = '<<column to split>>'
Declare #lsql nvarchar(max)
Declare #treeDepth int
If Object_id('dbo.treeDepth') is not null
Drop table dbo.treeDepth
CREATE TABLE dbo.treeDepth (depth INT)
declare #ltext nvarchar(max)= 'Select max(1+(len('+#ColumnToSplit+')- len(Replace('+#ColumnToSplit+','''+#Delimiter+''','''')))/(len('''+#Delimiter+'''))) from '+#InputTable
insert dbo.treeDepth EXEC(#ltext)
Select #lsql = isnull(#lsql+',','') +
'xmlname.value(''/Node[1]/Node['+cast(number+1 as nvarchar)+']'',''varchar(1000)'') AS Col_'+cast(number+1 as nvarchar)+''
from master..spt_values where type = 'P' and number < (Select * from dbo.treeDepth)
set #lsql = '
WITH ForXML
AS
(
SELECT *,
CONVERT(XML,''<Node><Node>''
+ REPLACE('+#ColumnToSplit+','''+#Delimiter+''', ''</Node><Node>'') + ''</Node></Node>'') AS xmlname
FROM '+#InputTable+'
)
Select *, '+#lsql+' Into '+#OutputTable+' From ForXML
Alter table '+#OutputTable+'
Drop column xmlname
'
EXEC(#lsql)

Updating column values as per our format

There are two types of records in my Db such as MS-NW and CS in the same column of table DICIPLINE I want to wrap if its CS (ANY TWO STRING LIKE CS,TE OR THE LIKE) then wrap it to BS(CS) (OR BS(TE) ETC) or if its MS-NW (Or MS-CS, MS-TE and the like) then wrap it to MS(NW) from the column dicipline.
I updated for two strings successfully and following is the query for that kindly let me know how can i do it for values like MS-NW OR MS-CS and convert it to the format like MS(NW) from following query .
UPDATE DEG set DICIPLINE = concat("BS(",DICIPLINE,")") where CHAR_LENGTH(DICIPLINE) = 2
The below query helps you to update your data.
update deg set DISIPLINE = if(length(DISIPLINE)= 2,concat('BC(',DISIPLINE,')')
,concat('MS(',substr(DISIPLINE, 4,4),')'));
See Sqlfiddle demo.
For safety, create a temporary column of same type and perform an update like this:
UPDATE deg
SET dicipline_temp = CASE
WHEN CHAR_LENGTH(dicipline) = 2
THEN CONCAT('BS(', dicipline, ')')
WHEN CHAR_LENGTH(dicipline) = 5 AND SUBSTRING(dicipline, 3, 1) = '-'
THEN CONCAT(REPLACE(dicipline, '-', '('), ')')
END
WHERE CHAR_LENGTH(dicipline) = 2 OR (CHAR_LENGTH(dicipline) = 5 AND SUBSTRING(dicipline, 3, 1) = '-')
If results are acceptable, update the actual column.