PL/SQL varchar(10) to varchar(9) - sql

How would I write a loop that has a select in the “in” clause of the loop that selects a col of type varchar(10), but then inserts those values in a col that wants them to be varchar(9)? Basically I’m trying to “typecast” from one precision to another, if that makes any sense. Example:
FOR V_TEN IN (SELECT THIS_IS_VARCHAR_TEN FROM TABLE WHERE SOMETHING=’VALUE’)
LOOP
INSERT INTO OTHER_TABLE
(THIS_IS_VARCHAR_NINE)
VALUES
(V_TEN);
END LOOP;
The error is that the column types aren’t the same. I’ve tried looking at to_char() and cast() but neither seem to be what I want. I realize there is a loss of precision here and am okay with that, since I actually know that the values in the varchar(10) column are always going to be 9 chars.

You are looking for the SUBSTR function.
Also, do not use PL/SQL for this, plain SQL will do and be faster.
INSERT INTO OTHER_TABLE
SELECT OTHER_COLUMN, SUBSTR(THIS_IS_VARCHAR_TEN,1,9)
FROM TABLE WHERE SOMETHING=’VALUE’;
And if there are really no values longer than nine character, you do not even need to call the substr function (it will be converted automatically, and raise an error if too long).

since I actually know that the values
in the varchar(10) column are always
going to be 9 chars.
If that's true, then you don't even need to use SUBSTR as others have been suggesting.
I believe the reason that you're getting an error is that you are trying to insert the value of V_TEN. When you use a construct like FOR x IN (SELECT ...) LOOP, x is implicitly declared as a record type. In your case, it's a record with only one field, but you still can't use it directly as a scalar type.
You just need to reference the field of the record by name in your insert.
FOR V_TEN IN (SELECT THIS_IS_VARCHAR_TEN FROM TABLE WHERE SOMETHING=’VALUE’)
LOOP
INSERT INTO OTHER_TABLE
(THIS_IS_VARCHAR_NINE)
VALUES
(V_TEN.THIS_IS_VARCHAR_TEN);
END LOOP;
In any case, as Thilo pointed out, there's no reason to do this in an explicit loop at all. Just write it as a single INSERT ... SELECT.

Use:
FOR V_TEN IN (SELECT SUBSTR(t.this_is_varchar_ten, 1, 9)
FROM TABLE t
WHERE t.something = 'VALUE')
LOOP
INSERT INTO OTHER_TABLE
(THIS_IS_VARCHAR_NINE)
VALUES
(V_TEN);
END LOOP;
Use the SUBSTR function to substring the VARCHAR(10) data so it is returned as VARCHAR(9)

Related

Remove NULL values and corresponding column names from SQL Insert statements

I very frequently have to deal with sql written by others.
A typical sql insert statements can hold 5-6 uneeded values (NULL).
An example:
INSERT INTO Texts (Text_EN, Text_NO, Text_DK, Text_SV) VALUES ('English', NULL, NULL, 'Svenska')
I've been looking for a way to transform a line like this into:
INSERT INTO Texts (Text_EN, Text_SV) VALUES ('English', 'Svenska')
So what I want to accomplish is:
Find the NULL values
Find the columns that correspond to those values
Remove those values and the column names
I've been trying various regexes with positive lookbehinds, since I think I'd need to find the NULLs first, but no dice. Perhaps there's another way already?
I've been checking out sql formatters as well, but they doesn't seem to have this very specific functionality.
You can do it by using dynamic SQL. For example, write function for 2 input array type parameters. One of the arrays is for fields and another array for values. Inside the function, you can declare 2 string-type variables, for dynamic SQLs, and use one for-loop operation. In this loop check values by the if operation. If the value is null then don't add to string values and fields, if-else add. After the loop is set to your main string variable 'insert into table' then execute dynamic SQL.
Shortly example:
declare
v_sql varchar(1000);
v_fields varchar(1000);
v_valuesm varchar(1000);
begin
-- for-loop statement
-- if check generate striong variables for fiedls and values
-- end of for-loop statement
v_sql = 'INSERT INTO Texts (' + v_fields + ') VALUES (' + v_values + ')';
-- executing your v_sql

String_Split inserts only the first value

I'm trying to insert comma separated Guids into a temp table, to later check for a value using IN in these Guids. The following query is inserting only the first value in the table twice.
DECLARE #campaignids nvarchar(max) = '1DEBD122-FF1B-4E87-8812-D427ABA5D54E,FBD06A2E-24D1-4C06-B71D-B4306D8EA3BD'
DECLARE #TempCampaignIds TABLE (CampaignId uniqueidentifier)
INSERT INTO #TempCampaignIds
SELECT CAST(#campaignids AS uniqueidentifier)
FROM STRING_SPLIT(#campaignids, ',')
SELECT CampaignId FROM #TempCampaignIds
--result
CampaignId
1DEBD122-FF1B-4E87-8812-D427ABA5D54E
1DEBD122-FF1B-4E87-8812-D427ABA5D54E
You need to use the value from the string:
INSERT INTO #TempCampaignIds (CampaignId)
SELECT CAST(s.value AS uniqueidentifier)
FROM STRING_SPLIT(#campaignids, ',') s;
Here is a db<>fiddle.
I'm actually surprised that your code works, but SQL Server converts the first value of such a string without an error. That doesn't seem to happen for other data types. In fact, SQL Server appears to look at only the first 36 characters for a unique identifier.

Trigger to convert empty string to 'null' before it posts in SQL Server decimal column

I've got a front table that essentially matches our SSMS database table t_myTable. Some columns I'm having problems with are those with numeric data types in the db. They are set to allow null, but from the front end when the user deletes the numeric value and tries to send a blank value, it's not posting to the database. I suspect because this value is sent back as an empty string "" which does not translate to the null allowable data type.
Is there a trigger I can create to convert these empty strings into null on insert and update to the database? Or, perhaps a trigger would already happen too late in the process and I need to handle this on the front end or API portion instead?
We'll call my table t_myTable and the column myNumericColumn.
I could also be wrong and perhaps this 'empty string' issue is not the source of my problem. But I suspect that it is.
As #DaleBurrell noted, the proper place to handle data validation is in the application layer. You can wrap each of the potentially problematic values in a NULLIF function, which will convert the value to a NULL if an empty string is passed to it.
The syntax would be along these lines:
SELECT
...
,NULLIF(ColumnName, '') AS ColumnName
select nullif(Column1, '') from tablename
SQL Server doesn't allow to convert an empty string to the numeric data type. Hence the trigger is useless in this case, even INSTEAD OF one: SQL Server will check the conversion before inserting.
SELECT CAST('' AS numeric(18,2)) -- Error converting data type varchar to numeric
CREATE TABLE tab1 (col1 numeric(18,2) NULL);
INSERT INTO tab1 (col1) VALUES(''); -- Error converting data type varchar to numeric
As you didn't mention this error, the client should pass something other than ''. The problem can be found with SQL Profiler: you need to run it and see what exact SQL statement is executing to insert data into the table.

Optimization of a substring query with charindex to trim the left part of a string

I need to get a substring of xyzdf/1234 resulting in 1234 (i.e. trimming the left part of the slash / ) . I have used
substring('xyzdf/1234',charindex('/','xyzdf/1234')+1,len('xyzdf/1234')-charindex('/','xyzdf/1234'))
which works but it is repetitive...
then I have used this way:
stuff('xyzdf/1234',1,charindex('/','xyzdf/1234'),'') and it works too and it is more compact, but still repeats the same argument twice 'xyzdf/1234'.
I wonder what would be the faster way to trim the left part. I will need to clean data in one column for million records. Not sure if the stuff command is faster enough. (Mind you it is a bulk operation). Thanks!
You could select the string from a VALUES.
That way you can repeat the value without double hardcoding it.
Then get the right part with the number from it.
F.e. using RIGHT, CHARINDEX, REVERSE and VALUES:
select right(val, charindex('/',reverse(val))-1) as nr
from (values ('xyzdf/1234')) q(val);
Or use SUBSTRING, CHARINDEX, LEN and VALUES:
select substring(val,charindex('/',val)+1,len(val)) as nr
from (values ('xyzdf/1234')) q(val);
Or abuse PARSENAME:
select parsename(replace('xyzdf/1234','/','.'),1) as nr;
Or use variables:
declare #value varchar(30) = 'xyzdf/1234';
declare #nr int = right(#value, charindex('/',reverse(#value))-1);
select #nr as nr;
But if the intention is to update a column so that only the number remains?
Then using the SUBSTRING method is probably still the safest.
Because it would keep those without / untouched, and without crashing on an Invalid length parameter passed error.
Example:
declare #Table table (id int identity(1,1) primary key, col1 varchar(30));
insert into #Table (col1) values
('xyzdf/1234'),
('12345');
update #Table
set col1 = substring(col1,charindex('/',col1)+1,len(col1))
where col1 like '%/[0-9]%';
select * from #Table;

Insert empty string into INT column for SQL Server

A SAMPLE table has only one column ID of type int, default null.
In Oracle when I do:
insert into SAMPLE (ID) values ('');
the new record is added with blank value. But in SQL Server 2008, when I run the same insert statement, the new record has the value of 0.
Is there a way to force SQL Server 2008 to default blank string to NULL instead of 0 (for numerical type of columns)?
Assuming that your INSERT statement is part of a stored procedure re-used in many places of your application (or, perhaps, is a batch always constructed by the same part of the client code) and that the inserted value is a number passed as a string argument, you could modify the INSERT like this:
INSERT INTO SAMPLE (ID) VALUES (NULLIF(#argument, ''));
Use NULL instead.
insert into SAMPLE (ID) values (NULL);
How about another idea - define an INSTEAD OF INSERT Trigger.
Despite the fact that you're trying to insert a string, with this the operation is "intercepted", empty string is replaced by NULL, and the insert succeeds.
If you define this trigger on your table, then you can continue to insert empty string as before, with no other changes.
Edit: As Martin Smith points out, this effectively is a comparison to 0 (the equivalent of empty string as an int) meaning you won't be able to store 0 in this table. I leave this answer here in case that's acceptable to your situation - either that or re-do all your queries!
CREATE TRIGGER EmptyStringTrigger
ON [SAMPLE]
INSTEAD OF INSERT
AS
BEGIN
INSERT INTO [SAMPLE](ID)
SELECT CASE
WHEN ID = '' THEN NULL
ELSE ID
END
FROM inserted
END
SQL Fiddle example
You can't insert a 'string' into a int column. Oracle must be just handling that for you.
Just try inserting NULL if that's what you need.
insert into SAMPLE (ID) values (NULL);
One more option
insert into SAMPLE (ID) values (DEFAULT)