TSQL select a string, repeated N times - html

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.

Related

Searching for entry with id in comma separated list in mysql

I want to get entries from a mysql table, which contain a given id within a comma separated list. I want to use regular expressions and the LIKE selector.
My current approach looks like this
SELECT * FROM table WHERE list LIKE '%,0,%';
with the problem being that this ignores the first and last element in a list like '0,1,2,3'.
I've tried using the | or operator to test for all possible cases.
SELECT * FROM table WHERE list LIKE '(%,0,%)|(^0,%)';
I've tried this with and without the ^ character and with and without the parenthesis, but in all cases this approach didn't even match the characters in the middle. In fact, the or operator doesn't seem to be working in even the simplest expressions like
SELECT * FROM table WHERE list LIKE '%(1|2)%';
You should fix your data model! DO not store lists of things -- especially numbers -- in a string. SQL has a great data model for storing lists: it is called a table.
If you are stuck with someone else's really, really, really bad choice of dta model, you can work around in. MySQL has a handy function, find_in_set(), that does what you want:
WHERE find_in_set('0', list) > 0
Concatenate commas to the start and the end of the list:
SELECT * FROM table WHERE concat(',', list, ',') LIKE '%,0,%';

MySql delete where find_in_set not working

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.

MYSQL REGEXP with JSON array

I have an JSON string stored in the database and I need to SQL COUNT based on the WHERE condition that is in the JSON string. I need it to work on the MYSQL 5.5.
The only solution that I found and could work is to use the REGEXP function in the SQL query.
Here is my JSON string stored in the custom_data column:
{"language_display":["1","2","3"],"quantity":1500,"meta_display:":["1","2","3"]}
https://regex101.com/r/G8gfzj/1
I now need to create a SQL sentence:
SELECT COUNT(..) WHERE custom_data REGEXP '[HELP_HERE]'
The condition that I look for is that the language_display has to be either 1, 2 or 3... or whatever value I will define when I create the SQL sentence.
So far I came here with the REGEX expression, but it does not work:
(?:\"language_display\":\[(?:"1")\])
Where 1 is replaced with the value that I look for. I could in general look also for "1" (with quotes), but it will also be found in the meta_display array, that will have different values.
I am not good with REGEX! Any suggestions?
I used the following regex to get matches on your test string
\"language_display\":\[(:?\"[0-9]\"\,)*?\"3\"(:?\,\"[0-9]\")*?\]
https://regex101.com/ is a free online regex tester, it seems to work great. Start small and work big.
Sorry it doesn't work for you. It must be failing on the non greedy '*?' perhaps try without the '?'
Have a look at how to serialize this data, with an eye to serializing the language display fields.
How to store a list in a column of a database table
Even if you were to get your idea working it will be slow as fvck. Better off to process through each row once and generate something more easily searched via sql. Even a field containing the comma separated list would be better.

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

Microsoft Access string parsing

In MS Access 2003, I have the following values in a column
0-0-60
20-0-0-24S
20.5-0-0-24S
32-0-0
How can I write my query such that I can pull the 3rd item from the column, example the "60" from "0-0-60". Keep in mind that I've optionally got the 4th column also.
You will want to generalize this function for the delimiter and what position, but:
Public Function SplitString(inputString As String) As String
Dim TestArray() As String
TestArray = Split(inputString, "-")
Debug.Print TestArray(2)
SplitString = TestArray(2)
End Function
You can call in in your query like this:
SELECT SplitString([MyField]) AS Expr1
FROM MyTable;
Good Luck!
Will you ever have less than 3 items in each column ?
Really if you've got a very specific format of either 3 decimals or 4 decimals as values, I'd consider changing your schema to
Value1 decimal
Value2 decimal
Value3 decimal
Value4 decimal (NULLABLE)
but that's a little beside the point...
If you really are stuck with this, then its probably quicker to parse in your code, than it is to do some nasty left/right/instr work in the Access SQL Query
The fact you are finding it non-trivial to query you data column is what we call a 'smell'.
This looks exactly like a First Normal Form (1NF) violation because you have non-scalar data in a single column. I trust you are splitting these values for a one-time data scrubbing exercise to fix the problem once and for all (in which case I'd personally use Excel's text to columns feature if the resultset was small).
If you are proposing to do this in a query for you application then I suggest you research normalization and change you schema accordingly.
I believe an inline expression like this on the query builder tool would do the job (untested).
right(yourColumnNam, inStrRev(yourColumnName, '-'))