SQL to update rows to remove words with less than N characters - sql

I have a TAGS column in my Products table in SQL Server. In my web project I split them with space, for example:
"web web_design website website_design"
=> 1.web 2. web_design 3. website ......
How I can remove words with less than N characters from tags? Is it possible with a regex?
For example if N=4 so "web" will be removed from my example and the rest remains.

I will give a solution to do this without changing your design at the bottom of this answer,
but I really think you should fix the design, here is an example on how to do that.
It starts from a table called "mytable" that has your "tag" column with all the data,
then it creates a detail table and populates it with the splitted values from your tag column
and then it is very easy to do what you want to do
create table tags (id int identity primary key, mytable_id int, tag varchar(100))
insert into tags (mytable_id, tag)
select t.id,
value
from mytable t
cross apply string_split(t.tag, ' ')
alter table mytable drop column tag
See a complete example in this dbfiddle
EDIT
if you need to show it again as if it where in one table, you can use string_agg like this
select m.id,
m.name,
( select string_agg(t.tag, ' ')
from tags t
where t.mytable_id = m.id
) as tags
from mytable m
You can see this at work in this dbfiddle
EDIT 2
And if you really want to stick to your design, here is how you can remove the words from your tag column
But I recommend not doing this, as you can see in the examples above it is not so hard to fix the design and create a new table to hold the tags.
update m
set m.tag =
( select string_agg(value, ' ')
from mytable t
cross apply string_split(m.tag, ' ')
where len(value) > 3
and t.id = m.id
)
from mytable m
Look at this dbfiddle to see it in action

Related

Loop through table and update a specific column

I have the following table:
Id
Category
1
some thing
2
value
This table contains a lot of rows and what I'm trying to do is to update all the Category values to change every first letter to caps. For example, some thing should be Some Thing.
At the moment this is what I have:
UPDATE MyTable
SET Category = (SELECT UPPER(LEFT(Category,1))+LOWER(SUBSTRING(Category,2,LEN(Category))) FROM MyTable WHERE Id = 1)
WHERE Id = 1;
But there are two problems, the first one is trying to change the Category Value to upper, because only works ok for 1 len words (hello=> Hello, hello world => Hello world) and the second one is that I'll need to run this query X times following the Where Id = X logic. So my question is how can I update X rows? I was thinking in a cursor but I don't have too much experience with it.
Here is a fiddle to play with.
You can split the words apart, apply the capitalization, then munge the words back together. No, you shouldn't be worrying about subqueries and Id because you should always approach updating a set of rows as a set-based operation and not one row at a time.
;WITH cte AS
(
SELECT Id, NewCat = STRING_AGG(CONCAT(
UPPER(LEFT(value,1)),
SUBSTRING(value,2,57)), ' ')
WITHIN GROUP (ORDER BY CHARINDEX(value, Category))
FROM
(
SELECT t.Id, t.Category, s.value
FROM dbo.MyTable AS t
CROSS APPLY STRING_SPLIT(Category, ' ') AS s
) AS x GROUP BY Id
)
UPDATE t
SET t.Category = cte.NewCat
FROM dbo.MyTable AS t
INNER JOIN cte ON t.Id = cte.Id;
This assumes your category doesn't have non-consecutive duplicates within it; for example, bora frickin bora would get messed up (meanwhile bora bora fickin would be fine). It also assumes a case insensitive collation (which could be catered to if necessary).
In Azure SQL Database you can use the new enable_ordinal argument to STRING_SPLIT() but, for now, you'll have to rely on hacks like CHARINDEX().
Updated db<>fiddle (thank you for the head start!)

Can TRIM function in SQL be more precise?

I have an import table that I want to split into other tables. There's a column in the import table which the data of it's fields have this format: ['email', 'phone', 'facebook'].
In order to separate each value on the field (email, phone and facebook in this case) I'm using the function TRIM when I insert into it's corresponding table, like this:
insert into Media (media)
select distinct trim ('{}' from regexp_split_to_table(host_media, ','))
from ImportH;
But the data inserted into the new table looks dirty, for example in row 1 I would have ['email', in row 2: 'phone' and in row 3: 'facebook'].
How can I make it so the data inserts into the table in a clean way, without the '[' and the floating commas?
I'll provide an image of the import table data of this column and what I get when I split it:
You could just change the splitter:
select *
from regexp_split_to_table('[''email'', ''phone'', ''facebook'']', '[^_a-zA-Z]+') s(value)
where value <> '';
The splitters are just whatever characters are NOT valid characters for the strings you want.
Here is a db<>fiddle.
One of the options... Maybe it will be ok for you:
with trimed as
(select substring(host_media from 2 for length(host_media)-2) as clean_col
from ImportH
)
insert into Media (media)
select unnest(string_to_array(clean_col, ','))
from trimed;
Here is a demo
I have understood the "floating commas" term little bit late so I added some changes to my query:
with trimed as
(select replace(substring(host_media from 2 for length(host_media)-2), '''', '') as clean_col
from ImportH
)
insert into Media (media)
select ltrim(unnest(string_to_array(clean_col, ',')))
from trimed;
Here is a different approach which uses json functions.
First create media table;
CREATE TABLE media AS (
WITH my_medias AS (
SELECT
jsonb_array_elements_text(jsonb_agg(DISTINCT medias ORDER BY medias)) media
FROM impor_th, jsonb_array_elements_text(replace(host_media, '''', '"')::jsonb) medias
)
SELECT
row_number() OVER () media_id,
*
FROM my_medias
);
Then create many-to-many relation table;
CREATE TABLE media_host AS (
WITH elements AS (
SELECT
id,
jsonb_array_elements_text(replace(host_media, '''', '"')::jsonb) media
FROM impor_th i
)
SELECT
e.id AS impor_th_id,
m.media_id
FROM elements e
JOIN media m ON m.media = e.media
);

Search for a word in the column string and list those words

Have two tables, table 1 with columns W_ID and word. Table 2 with column N_ID and note. Have to list all the NID where words found in table 1 word column contains in Note column (easy part) and also list those words in another column without duplicating the N_ID. Which means using STUFF to concatenate all the words found in Note column for that particular N_ID. I tried using
FULL TEXT INDEX using CONTAIN
But it only allows to search for one word at a time. Any suggestions how I can use a while loop to achieve this.
If there is a maximum number of words you want displayed for N_ID, you can pivot this. You could have them in a single column by concatenating them, but I would recommend against that. Here is a pivot that supports up to 4 words per N_ID. You can adjust it as needed. You can view the SQL Fiddle for this here.
SELECT
n_id,
[1] AS word_1,
[2] AS word_2,
[3] AS word_3,
[4] AS word_4
FROM (
SELECT
n_id,
word,
ROW_NUMBER() OVER (PARTITION BY n_id ORDER BY word) AS rn
FROM tbl2
JOIN tbl1 ON
tbl2.note LIKE '%'+tbl1.word+'[ ,.?!]%'
) AS source_table
PIVOT (
MAX(word)
FOR rn IN ([1],[2],[3],[4])
) AS pivot_table
*updated the join to prevent look for a space or punctuation to declare the end of a word.
You can join your tables together based on a postive result from the charindex function.
In SQL 2017 you can run:
SELECT n_id, string_agg(word)
FROM words
inner join notes on 0 < charindex(words.word, notes.note);
Prior to SQL 2017, there is no string_agg so you'll need to use stuff, which is trickier:
select
stuff((
SELECT ', ' + word
FROM words
where 0 < charindex(words.word, notes.note)
FOR XML PATH('')
), 1, 2, '')
from notes;
I used the following schema:
CREATE table WORDS
(W_ID int identity primary key
,word varchar(100)
);
CREATE table notes
(N_ID int identity primary key
,note varchar(1000)
);
insert into words (word) values
('No'),('Nope'),('Nah');
insert into notes (note) values
('I am not going to do this. Nah!!!')
,('It is OK.');

how to replace a column value which is separated by comma in SQL

I am having a table which is having a column named as CDR.
In that CDR column we have values stored as comma separated like 20,5,40,10,30
I just need to replace last value(here it is 30) to 0 in every row.
Can someone suggest me how can we do?
Thanks
If you are able, first correct the database design as the table is not in first normal form. It is bad design to have more than one value stored in one column, as evidenced by you having to ask this question. :-) Having said that, I have to deal with vendor data that has the same issue that is beyond my control to change, so in Oracle 11g I would do this:
update table_name
set CDR = regexp_replace(CDR, '(.*,)\d+$', '\10');
The regex matches and remembers all characters up to and including the last comma before one or more digits right before the end of the string. The replace string is the remembered part referenced by the \1, referring to the first grouping of characters inside parenthesis), plus the 0.
If you are using SQL Server, this should do for you.
create table #A(id int , cdr varchar(100))
insert into #A values(1,'10,20,30,40'),(2, '20,30,40,50'),(3,'30,40,50,60,70')
Declare #tA as table(id int , String varchar(10))
insert into #tA
SELECT id,
Split.a.value('.', 'VARCHAR(100)') AS String
FROM (SELECT [id],
CAST ('<M>' + REPLACE([cdr], ',', '</M><M>') + '</M>' AS XML) AS String
FROM #A) AS A CROSS APPLY String.nodes ('/M') AS Split(a);
delete from #tA where [String] = '30'
SELECT distinct id,
ISNULL(STUFF((SELECT ', ' + String
FROM #tA t
WHERE t.id = ta.id
FOR XML PATH('')
), 1, 1, ''), '') AS Str
into #tempA
FROM #tA ta
select * from #tempA
drop table #A, #tempA
UPDATE TableName
SET CDR = REPLACE(CDR, (SUBSTRING( CDR, LEN(CDR) - CHARINDEX(',',REVERSE(CDR)) + 2 , LEN(CDR))),0);
You should think about splitting up your comma separated list into a separate table. That way you can do other things in SQL. SQL is not the best with string manipulation and your queries are gonna get obscene and unruly.
table Users
user_id user_name job_list
1 Billy "1,2,3,4"
table Jobs
job_id job_desc
1 plumber
2 carpenter
3 electrician
4 programmer
If you do this you're gonna have some heartaches where a job goes away or something you're gonna have a lot of annoying cleanup like #jarlh suggests.
If you make a third table to hold the relationships user_id to job_id you will have a much better time if you need to do something like delete a job_id from existence. Of course this is all made up based on your limited question, but it should help you out.
table UserJobRelationship
relationship_id user_id job_id
1 1 1
2 1 2
3 1 3
4 1 4
Gives you much more flexibility and allows you to delete the most recent entry. You can simply just do max of relationship_id where user_id equals that user or you can do it for the whole table.

How to generate a hyperlink tag to each string in column, to create a tag cloud

I have a table in SQL Server that stores info from a blog, so each record is a post. One of the columns stores the tags related to each post.
I need to create a tag for each tag stored in that column, they are separated with a comma ','.
For example: For the record 1, the column "tags" stores "cars,planes,boats".
I need to generate a column upon SELECT command that will write this:
cars<a><a href="blog-tags.aspx?tag=planes">planesboats
Any help would be highly appreciated! Thanks
If there's some kind of coding layer between the data and the user, then it's probably best to do this in the coding layer.
If you really must do it in sql, you could first convert the column into a separate temporary table, then join with the temporary table (and select accordingly)
INSERT INTO #tempTable(primaryKey, data)
SELECT yt.primaryKey, s.data
FROM
YourTable yt
inner join dbo.Split(yt.tags, ',') s
Relies on a split function such as found here: split function (which isn't a very fast one... but will suffice)
Then....
Select yt.*,
'<a href="blog-tags.aspx?tag=' + t.data + '">' + t.data + '<a>' as Link
from
YourTable yt
inner join #tempTable t on yt.PrimaryKey = t.PrimaryKey