Complex Find and Replace in SQL - sql

I have a list of ProductName key/value pairs, like so:
CREATE TABLE [dbo].[ProductNames]
(
[Key] [nvarchar](100) NULL,
[Value] [nvarchar](100) NULL
)
Containing values like this:
'Pepsi', 'Pepsi®'
'Coke', 'Coke®'
I need to find and replace values in a text field that DO NOT ALREADY contain the registered trademark for a product...and replace it with the full trademark string.
FOR EXAMPLE:
1. if 'Pepsi' is alone....it becomes 'Pepsi®'
2. if 'Pepsi®' already exists...do nothing
UPDATE:
The registered trademark is only one example of something to be replaced. There could be other multi-character replacements as well. As such, something more complex is probably needed. For example, I would probably detect 'Pepsi', then truncate 'Pepsi' from the VALUE portion of the key/value row...AND THEN...try to detect if that truncated value already exists in the string. If not, then go ahead and replace the value (something like that).

Perhaps this solution will help you. It will update records where value does not end with ®, appending it at the end.
update ProductNames
set Value = Value + '®'
where Value not like '%®'

update ProductNames set
Value = CASE WHEN CHARINDEX('®',value,1) = 0 then value+'®' else value end

I can't think of an easy way for this to work. Iterate through each special keyword and check the beginning, middle and end cases for these words, assuming you require white space to separate each keyword. Break down the operation into a few steps, as required by business logic. Expensive yes, but it's a one time thing, I assume. Better yet, run this routine once when a row is added/modified.

Related

Why does an int column values defaults to '0' when passed empty?

I have table with an integer column.
CREATE TABLE [dbo].[tble1](
[id] [int] NOT NULL,
[test] [nchar](10) NULL
)
When I try to insert some values and pass an empty string to the id column like below, it gets inserted and the value of the id column is 0 by default.
INSERT INTO [dbo].[tble1]
([id],[test])
VALUES
('','a')
I couldn't find any satisfying reasoning behind it. Could some one please share your thoughts on this?
What is happening is that '' is being converted to an integer. The rules are that a string can be converted, based on the digit characters in the string.
If a string is empty, it gets converted to 0.
So, the conversion is happening at the very "top" level. The types don't match so SQL Server attempts an implicit conversion.
Unfortunately, the documentation is not really clear on the topic:
Character expressions that are being converted to an exact numeric
data type must consist of digits, a decimal point, and an optional
plus (+) or minus (-). Leading blanks are ignored. Comma separators,
such as the thousands separator in 123,456.00, are not allowed in the
string.
To be honest, I would interpret the "must consist of digits" as saying that there must be at least one digit (although technically in English "zero" is treated as a plural, I don't necessarily think of plurals as including zero elements). However, the empty string has been used -- pretty much for forever -- as a valid value for any type across a broad range of databases.
It will try to Convert ' ' to Integer and it got succeeded.
SELECT CONVERT(INT, '')
Output
0
You are getting defaulted value to 0, as you have NOT null, defined for the column,
if you keep ID as null, then it will put NULL,
Also, if you want to populate the value automatically then you set the identity for the column

SQL: How to apply a function (stored procedure) within an UPDATE-clause to change values?

the following function deletes all blanks in a text or varchar column and returns the modified text/varchar as an int:
select condense_and_change_to_int(number_as_text_column) from mytable;
This exact query does work.
Though my goal is to apply this function to all rows of a column in order to consistently change its values. How would I do this? Is it possible with the UPDATE-clause, or do i need to do this within a function itself? I tried the following:
UPDATE mytable
SET column_to_be_modiefied = condense_and_change_to_int(column_to_be_modiefied);
Basically i wanted to input the value of the current row, modify it and save it to the column permanantly.
I'd welcome all ideas regarding how to solve scenarios like these. I'm working with postgresql (but welcome also more general solutions).
Is it possible with an update? Well, yes and sort-of.
From your description, the input to the function is a string of some sort. The output is a number. In general, numbers should be assigned to columns with a number type. The assumption is that the column in question is a number.
However, your update should work. The result will be a string representation of the number.
After running the update, you can change the column type, with something like:
alter table mytable alter column column_to_be_modiefied int;

Alter column from varchar to decimal when nulls exist

How do I alter a sql varchar column to a decimal column when there are nulls in the data?
I thought:
ALTER TABLE table1
ALTER COLUMN data decimal(19,6)
But I just get an error, I assume because of the nulls:
Error converting data type varchar to numeric. The statement has been terminated.
So I thought to remove the nulls I could just set them to zero:
ALTER TABLE table1
ALTER COLUMN data decimal(19,6) NOT NULL DEFAULT 0
but I dont seem to have the correct syntax.
Whats the best way to convert this column?
edit
People have suggested it's not the nulls that are causing me the problem, but non-numeric data. Is there an easy way to find the non-numeric data and either disregard it, or highlight it so I can correct it.
If it were just the presence of NULLs, I would just opt for doing this before the alter column:
update table1 set data = '0' where data is null
That would ensure all nulls are gone and you could successfully convert.
However, I wouldn't be too certain of your assumption. It seems to me that your new column is perfectly capable of handling NULL values since you haven't specified not null for it.
What I'd be looking for is values that aren't NULL but also aren't something you could turn in to a real numeric value, such as what you get if you do:
insert into table1 (data) values ('paxdiablo is good-looking')
though some may argue that should be treated a 0, a false-y value :-)
The presence of non-NULL, non-numeric data seems far more likely to be causing your specific issue here.
As to how to solve that, you're going to need a where clause that can recognise whether a varchar column is a valid numeric value and, if not, change it to '0' or NULL, depending on your needs.
I'm not sure if SQL Server has regex support but, if so, that'd be the first avenue I'd investigate.
Alternatively, provided you understand the limitations (a), you could use isnumeric() with something like:
update table1 set data = NULL where isnumeric(data) = 0
This will force all non-numeric values to NULL before you try to convert the column type.
And, please, for the love of whatever deities you believe in, back up your data before attempting any of these operations.
If none of those above solutions work, it may be worth adding a brand new column and populating bit by bit. In other words set it to NULL to start with, and then find a series of updates that will copy data to this new column.
Once you're happy that all data has been copied, you should then have a series of updates you can run in a single transaction if you want to do the conversion in one fell swoop. Drop the new column and then do the whole lot in a single operation:
create new column;
perform all updates to copy data;
drop old column;
rename new column to old name.
(a) From the linked page:
ISNUMERIC returns 1 for some characters that are not numbers, such as plus (+), minus (-), and valid currency symbols such as the dollar sign ($).
Possible solution:
CREATE TABLE test
(
data VARCHAR(100)
)
GO
INSERT INTO test VALUES ('19.01');
INSERT INTO test VALUES ('23.41');
ALTER TABLE test ADD data_new decimal(19,6)
GO
UPDATE test SET data_new = CAST(data AS decimal(19,6));
ALTER TABLE test DROP COLUMN data
GO
EXEC sp_RENAME 'test.data_new' , 'data', 'COLUMN'
As people have said, that error doesn't come from nulls, it comes from varchar values that can't be converted to decimal. Most typical reason for this I've found (after checking that the column doesn't contain any logically false values, like non-digit characters or double comma values) is when your varchar values use comma for decimal pointer, as opposed to period.
For instance, if you run the following:
DECLARE #T VARCHAR(256)
SET #T = '5,6'
SELECT #T, CAST(#T AS DEC(32,2))
You will get an error.
Instead:
DECLARE #T VARCHAR(256)
SET #T = '5,6'
-- Let's change the comma to a period
SELECT #T = REPLACE(#T,',','.')
SELECT #T, CAST(#T AS DEC(32,2)) -- Now it works!
Should be easy enough to look if your column has these cases, and run the appropriate update before your ALTER COLUMN, if this is the cause.
You could also just use a similar idea and make a regex search on the column for all values that don't match digit / digit+'.'+digit criteria, but i suck with regex so someone else can help with that. :)
Also, the american system uses weird separators like the number '123100.5', which would appear as '123,100.5', so in those cases you might want to just replace the commas with empty strings and try then?

SQL Checking Substring in a String

I have a table with column mapping which store record: "IV=>0J,IV=>0Q,IV=>2,V=>0H,V=>0K,VI=>0R,VI=>1,"
What is the sql to check whether or not a substring is in column mapping.
so, I would like this:
if I have "IV=>0J" would return true, because IV=>0J is exact in string "mapping"
if I have "IV=>01" would return false. And so on...
I try this:
SELECT * FROM table WHERE charindex('IV=>0J',mapping)
But when I have "IV=>0", it returns TRUE. But, it should return FALSE.
Thank You..
You can search with commas included. Just also add one at beginning and end of mapping:
SELECT * FROM table WHERE charindex(',IV=>0J,',',' + mapping + ',') <> 0
or
SELECT * FROM table WHERE ',' + mapping + ',' LIKE '%,IV=>OJ,%'
This should do the trick:
SELECT * FROM table
WHERE
mapping LIKE '%,IV=>0J,%'
OR mapping LIKE '%,IV=>0J'
OR mapping LIKE 'IV=>0J,%'
OR mapping = 'IV=>0J'
But you should really normalize the database - you are currently violating the principle of atomicity, and therefore the 1NF. Your current difficulties in querying and the future difficulties with performance that you are about to encounter all stem from this root problem...
While you can search by including a comma in the string, this is a bad design for several reasons.
You are unable to take advantage of indexing
You force a full scan of the table, which will lead to bad performance AND excessive blocking.
You have to make sure that there is always a leading or a trailing comma (depends on what you expect in your LIKE expression).
You are no longer able to edit a single entry, you'll have to replace the entire string each time you want to change even a single mapping.
You open yourself to a concurrency nightmare if more that one users try to update different mappings that just happen to be stored in the same column.
Your table isn't even in 1st normal form any more, which is why you have such difficulties
You should normalize your mapping column, by extracting the data to a different mapping table, with at least the From and To columns you require. You can then add these columns to an index an convert your query using only a single index seek.
You can also add the ID values of your source table to the Mappings table and the index. This will allow you to convert the lookup for a source row to a join between the two tables that takes advantage of indexing
charindex returns the position of the text, not Boolean.
to check if the text exists, compare to 0:
SELECT * FROM table WHERE charindex('IV=>0J',mapping) <> 0
I think you're missing something here, the Charindex function does not return TRUE or FALSE.
It returns the starting point of the substring inside master string, or if the substring is not present, then -1.
So you query should read,
SELECT * FROM table WHERE charindex('IV=>0J',mapping) > 0

MySQL - Set default value for field as a string concatenation function

I have a table that looks a bit like this actors(forename, surname, stage_name);
I want to update stage_name to have a default value of
forename." ".surname
So that
insert into actors(forename, surname) values ('Stack', 'Overflow');
would produce the record
'Stack' 'Overflow' 'Stack Overflow'
Is this possible?
Thanks :)
MySQL does not support computed columns or expressions in the DEFAULT option of a column definition.
You can do this in a trigger (MySQL 5.0 or greater required):
CREATE TRIGGER format_stage_name
BEFORE INSERT ON actors
FOR EACH ROW
BEGIN
SET NEW.stage_name = CONCAT(NEW.forename, ' ', NEW.surname);
END
You may also want to create a similar trigger BEFORE UPDATE.
Watch out for NULL in forename and surname, because concat of a NULL with any other string produces a NULL. Use COALESCE() on each column or on the concatenated string as appropriate.
edit: The following example sets stage_name only if it's NULL. Otherwise you can specify the stage_name in your INSERT statement, and it'll be preserved.
CREATE TRIGGER format_stage_name
BEFORE INSERT ON actors
FOR EACH ROW
BEGIN
IF (NEW.stage_name IS NULL) THEN
SET NEW.stage_name = CONCAT(NEW.forename, ' ', NEW.surname);
END IF;
END
According to 10.1.4. Data Type Default Values no, you can't do that. You can only use a constant or CURRENT_TIMESTAMP.
OTOH if you're pretty up-to-date, you could probably use a trigger to accomplish the same thing.
My first thought is if you have the two values in other fields what is the compelling need for redundantly storing them in a third field? It flies in the face of normalization and efficiency.
If you simply want to store the concatenated value then you can simply create a view (or IMSNHO even better a stored procedure) that concatenates the values into a pseudo actor field and perform your reads from the view/sproc instead of the table directly.
If you absolutely must store the concatenated value you could handle this in two ways:
1) Use a stored procedure to do your inserts instead of straight SQL. This way you can receive the values and construct a value for the field you wish to populate then build the insert statement including a concatenated value for the actors field.
2) So I don't draw too many flames, treat this suggestion with kid gloves. Use only as a last resort. You could hack this behavior by adding a trigger to build the value if it is left null. Generally, triggers are not good. They add unseen cost and interactions to fairly simple interactions. You can, though, use the CREATE TRIGGER to update the actors field after a record is inserted or updated. Here is the reference page.
As of MySQL 8.0.13, you can use DEFAULT clause for a column which can be a literal constant or an expression.
If you want to use an expression then, simply enclose the required expression within parentheses.
(concat(forename," ",surname))
There are two ways to accomplish what you are trying to do as per my knowledge:
(important: consider backing up your table first before running below queries)
1- Drop the column "stage_name" all together and create a new one with DEFAULT constraint.
ALTER TABLE actors ADD COLUMN stage_name VARCHAR(20) DEFAULT (concat(forename," ",surname))
2- This will update newer entries in the column "stage_name" but not the old ones.
ALTER TABLE actors alter stage_name set DEFAULT (concat(forename," ",surname));
After that, if you need to update the previous values in the column "stage_name" then simply run:
UPDATE actors SET stage_name=(concat(forename," ",surname));
I believe this should solve your problem.