MySql delete where find_in_set not working - mysql

I have a MySql stored procedure that has multiple parts. Procedure receives an INT "inId" and a VARCHAR(500) argument called "inIgnoreLogTypes" that's a comma-separated list of numbers.
First part of SQL looks like this:
DECLARE affectedNumbers text;
SELECT GROUP_CONCAT(am.Numbers) INTO affectedNumbers FROM Users am WHERE am.userID = inId;
I need to do that because variable "affectedNumbers" will be used later on throughout this rather big stored procedure so for sake of performances i don't wanna do "IN (Select ...)" every time i need to look up the list.
I checked, variable "affectedNumbers" get's correctly populated with comma separated values.
Next part is this (and that's where the problem occurs):
DELETE FROM UserLogs WHERE
FIND_IN_SET(User_Number, affectedNumbers) AND
NOT FIND_IN_SET(LogType, inIgnoreLogTypes);
Above statement does nothing and after hours of searching for "why" i can't find the answer... Maybe because "affcetedNumbers" is TEXT and "User_Number" is INT? Or maybe because "LogType" is INT and "inIgnoreLogTypes" is VARCHAR?
I checked both sets, they are comma separated integers...

Found the issue! I have to use something like this:
DELETE FROM UserLogs WHERE
FIND_IN_SET(UserLogs.User_Number, affectedNumbers) AND
NOT FIND_IN_SET(UserLogs.LogType, inIgnoreLogTypes);
Strange, as there were no errors.... Now it works.

Related

MySql SELECT found row when WHERE doesn't match

I have a pretty simple table called roles:
When I ran SELECT * FROM roles WHERE role_id = "1ojosd041l"(the WHERE clause clearly didn't match with any row in the table). It somehow returned this:
Does anyone know why this is happening? My guess is that my role_id column is of type int, but I passed a string into the WHERE clause, so the str to int conversion produced something weird (like a 0 or 1).
I'm just looking for an official explanation for this. If anyone knows why this happens please let me know! Thank you!
Where MySQL expects a number, it will convert a string to a number, and ignore any leading whitespace or trailing garbage.
If you want to avoid this, do something like
SELECT * FROM roles WHERE role_id = "1ojosd041l" and concat(role_id) = "1ojosd041l"
Just the second condition would be enough but leaving the first condition in allows an index to be used.
There is somee MySQL magic happening here. MySQL sees that role_id is numeric. So it converts your string '1ojosd041l' to a number. This string isn't a number obviously, and MySQL should throw an error. But MySQL just takes as many digits as it finds from the left side of your string instead, so it gets number 1. (If your string started with a non-digit, then MySQL would even return a zero.)

Join returns NULL when data that matches is in the table

I'm trying to get results when both tables have the same machine number and there are entries that have the same number in both tables.
Here is what I've tried:
SELECT fehler.*,
'maschine.Maschinen-Typ',
maschine.Auftragsnummer,
maschine.Kunde,
maschine.Liefertermin_Soll
FROM fehler
JOIN maschine
ON ltrim(rtrim('maschine.Maschinen-Nr')) = ltrim(rtrim(fehler.Maschinen_Nr))
The field I'm joining on is a varchar in both cases. I tried without trims but still returns empty
I'm using MariaDB (if that's important).
ON ltrim(rtrim('maschine.Maschinen-Nr')) = ltrim(rtrim(fehler.Maschinen_Nr)) seems wrong...
Is fehler.Maschinen_Nr really the string 'maschine.Maschinen-Nr'?
SELECT fehler.*, `maschine.Maschinen-Typ`, maschine.Auftragsnummer, maschine.Kunde, maschine.Liefertermin_Soll
FROM fehler
JOIN maschine
ON ltrim(rtrim(`maschine.Maschinen-Nr`)) = ltrim(rtrim(`fehler.Maschinen_Nr`))
Last line compared a string to a number. This should be doing it.
Also, use the backtick to reference the column names.
The single quotes are string delimiters. You are comparing fehler.Maschinen_Nr with the string 'maschine.Maschinen-Nr'. In standard SQL you would use double quotes for names (and I think MariaDB allows this, too, certain settings provided). In MariaDB the commonly used name qualifier is the backtick:
SELECT fehler.*,
`maschine.Maschinen-Typ`,
maschine.Auftragsnummer,
maschine.Kunde,
maschine.Liefertermin_Soll
FROM fehler
JOIN maschine
ON trim(`maschine.Maschinen-Nr`) = trim(fehler.Maschinen_Nr)
(It would be better of course not to use names with a minus sign or other characters that force you to use name delimiters in the first place.)
As you see, you can use TRIM instead of LTRIM and RTRIM. It would be better, though, not to allow space at the beginning or end when inserting data. Then you wouldn't have to remove them in every query.
Moreover, it seems Maschinen_Nr should be primary key for the table maschine and naturally a foreign key then in table fehler. That would make sure fehler doesn't contain any Maschinen_Nr that not exists exactly so in maschine.
To avoid this problems in future, the convention for DB's is snake case(lowercase_lowercase).
Besides that, posting your DB schema would be really helpfull since i dont guess your data structures.
(For friendly development, is usefull that variables, tables and columns should be written in english)
So with this, what is the error that you get, because if table "maschine" has a column named "Maschinen-Nr" and table "fehler" has a column named "Maschinen_Nr" and the fields match each other, it should be correct
be careful with Maschinen-Nr and Maschinen_Nr. they have - and _ on purpose?
a very blind solution because you dont really tell what is your problem or even your schema is:
SELECT table1Alias.*, table2Alias.column_name, table2Alias.column_name
FROM table1 [table1Alias]
JOIN table2 [table2Alias]
ON ltrim(rtrim(table1Alias.matching_column)) = ltrim(rtrim(table2Alias.matching_column))
where matching_columns are respectively PK and FK or if the data matches both columns [] are optional and if not given, will be consider table_name

Inserting a delimiter

MySql has a function CONCAT_WS that I use to export multiple fields with a delimiter into a single field. Works great!
There are multiple fields being stored in a database I query off of that has data that I need to extract each field individually but within each field the data need to include a delimiter. I can most certainly do a concatenate but that does take awhile to set-up if my data requires up to 100 unique values. Below is an example of what I am talking about
Stored Data 01020304050607
End Result 01,02,03,04,05,06,07
Stored Data 01101213
End Result 01,10,12,13
Is there a function in MySQL that does the above?
I am not that familiar with mysql but I have seen questions like this come up before where a regular expression function would be useful. There are user-defined functions available that allow Oracle-like regular expression functions to be used as their support is weak in mysql. See here: https://github.com/hholzgra/mysql-udf-regexp
So you could do something like this:
select trim(TRAILING ',' FROM regexp_replace(your_column, '(.{2})', '\1,') )
from your_table;
This adds a comma every 2 character then chops off the last one. Maybe this will give you some ideas.

TSQL select a string, repeated N times

I am looking for a way, inside a TSQL SELECT statement, to repeat a string literal N times, where N is in integer that I select (or calculate) from columns on a table.
What I am doing, specifically, is parsing a hierarchical tree structure into nested HTML unordered lists. So, suppose we're very deep in the tree, but the next entry is very shallow. We need to close a bunch of open <ul> and <li> tags from higher up in the tree. So what I have done (but I feel is too kludgy) is:
select case Depth-Next_Depth
when -1 then ''
when 0 then ''
when 1 then '</ul></li>'
when 2 then '</ul></li></ul></li>'
when 3 then '</ul></li></ul></li></ul></li>'
...
end
from MyTable
I know that I could create a scalar function like ReturnClosingTags(#N int) but I don't want to add another object to the database for this. I want it to all happen in the select statement so I don't have to further modify the DB schema.
I also know that my whole approach here (writing the code myself to parse my tree into HTML) may be harebrained, so feel free to comment with an alternative... but I am interested in the question for its own sake, so even if you say "duh just use .NET library XYZ for this" I'd like to know if there's an answer to this little SQL puzzle.
SQL Server happens to have this function built-in: replicate().
select replicate('</ul></li>', depth)
Because it returns the same type as the first argument, you might want to cast() the value to varchar(8000) or varchar(max) first.

Converting a upper case database to proper case

I am new to SQL and I have several large database with upper case first and last names that I need to convert to proper case in SQL sever 2008.
I am using the following to do this:
update database
Set FirstNames = upper(substring(FirstNames, 1, 1))
+ lower(substring(FirstNames, 2, (len(FirstNames) - 1) ))
I was wondering if there was any way to adapt this so that a field with two first names is also updated (currently I make the change and then go through and manually change the second name).
I have looked over the other answers in this field and they all seem quit long, compared to the query above.
Also is there any way to assist with converting the Mc suranmes ( I will manually change the others)? MCDONALD to McDonald, again I am just using the about query but replacing the FirstNames with LastName.
This is probably best done outside of SQL. However, if there is a requirement to do it on the server or if speed isn't an issue (because it will be an issue so you need to figure out if you care), the way you are going about it is probably the best way of doing so. If you want, you could create a UDF that puts all of the logic in one area.
Here is some code I came across (with attribution and more information below it):
CREATE FUNCTION dbo.fCapFirst(#input NVARCHAR(4000)) RETURNS NVARCHAR(4000)
AS
BEGIN
DECLARE #position INT
WHILE IsNull(#position,Len(#input)) > 1
SELECT #input = Stuff(#input,IsNull(#position,1),1,upper(substring(#input,IsNull(#position,1),1))),
#position = charindex(' ',#input,IsNull(#position,1)) + 1
RETURN (#input)
END
--Call it like so
select dbo.fCapFirst(Lower(Column)) From MyTable
I got this code from http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=37760 There is more information and other suggestions in this forum as well.
As for dealing with cases like the McDonald, I would suggest one of two ways to handle this. One would be to put a search in the above UDF for key names ('McDonald', 'McGrew', etc.) or for patterns (the first two letters are Mc then make the next one capital, etc.) The second way would be to put these cases (the full names) in a table and have their replacement value in a second column. Then simply do a replace. Most likely, however, it will be easiest to identify rules like Mc then capitalize instead of trying to list every last-name possibility.
Don't forget you may want to modify the above UDF to include dashes, not just spaces.
Maybe this is too long but it is very easy and can be adapted for -, ', etc:
UPDATE tbl SET LastName = Case when (CharIndex(' ',lastname,1)<>0) then (Upper(Substring(lastname,1,1))+Lower(Substring(lastname,2,CharIndex(' ',lastname,1)-1)))+
(Upper(Substring(lastname,CharIndex(' ',lastname,1)+1,1))+
Lower(Substring(lastname,CharIndex(' ',lastname,1)+2,Len(lastname)-(CharIndex(' ',lastname,1)-1))))
else (Upper(Substring(lastname,1,1))+Lower(Substring(lastname,2,Len(lastname)-1))) end,
FirstName = Case when (CharIndex(' ',firstname,1)<>0) then (Upper(Substring(firstname,1,1))+Lower(Substring(firstname,2,CharIndex(' ',firstname,1)-1)))+
(Upper(Substring(firstname,CharIndex(' ',firstname,1)+1,1))+
Lower(Substring(firstname,CharIndex(' ',firstname,1)+2,Len(firstname)-(CharIndex(' ',firstname,1)-1))))
else (Upper(Substring(firstname,1,1))+Lower(Substring(firstname,2,Len(firstname)-1))) end;
Tony Rogerson has code that deals with:
double barrelled names eg Arthur Bentley-Smythe
Control characters
I haven't used it myself though...