DB2 V9 z/os
Background: I have a 4 column table defined as (col1 int, col2 smallint, col3 int, col4 date)
Row 1 has values of (1,123,456,2012-08-23)
When I execute the following:
SELECT CAST(col2 AS VARCHAR(5)) CONCAT CAST(col3 AS VARCHAR(5))
FROM db.T1
WHERE col1 = 1;
Value 123456 is returned, which is exactly what i want.
When I execute the following:
UPDATE db.table2
SET col3 = SELECT CAST(col2 AS VARCHAR(5)) CONCAT CAST(col3 AS VARCHAR(5))
FROM db.T1
WHERE col1 = 1;
Error is:
SQL0408N A value is not compatible with the data type of its assignment target. Target name is "col3". SQLSTATE=42821
I understand the error is due to attempting to insert a varchar into an integer. What else can I do? I've tried using various CAST statements but cannot get a value to insert into col3. i need the value to appear joined as shown above.
Any help would be appreciated.
Wrapping all of the casts as a final cast( ... as integer) should work:
UPDATE db.table2
SET col3 = SELECT CAST(
CAST(col2 AS VARCHAR(5)) CONCAT CAST(col3 AS VARCHAR(5))
AS INTEGER)
FROM db.T1
WHERE col1 = 1;
depend on the MAXIMUM value of your small integers,
you can convert them to base-9,
then concatenate with '9'.
CONCAT( IFNULL(CONV(sint1, 10, 9),''),
'9',
IFNULL(CONV(tint2, 10, 9),'')
) AS combined
and another option, ALSO depend on the MAXIMUM value of your small integers,
to PAD-LEFT with zeros one of them.
CONCAT(
sint1,
LPAD(tint2,3,0)
) AS combined
See full code and explanation: https://mdb-blog.blogspot.com/2021/10/mysql-joincombine-2-smallinttinyint.html
Related
This is a scalar query, originally within a function. The result datatype varies, depending on which field I'm intresting of.
In this example, I expect a scalar result of datatype NVARCHAR 'Andy' but got an error:
Msg 245, Level 16, State 1, Line xx Conversion failed when converting
the nvarchar value 'Andy' to data type int.
Is there any way to get around this?
CREATE TABLE ATable (
Idf INT PRIMARY KEY,
Col1 INT,
Col2 NVARCHAR(255),
)
GO
INSERT INTO Atable (Idf, Col1, Col2) VALUES (1, 75, 'Andy')
INSERT INTO Atable (Idf, Col1, Col2) VALUES (2, 39, 'Pete')
GO
DECLARE #Idf INT = 1
DECLARE #Col NVARCHAR(15) = 'Col2'
SELECT
CASE
WHEN #Col='Col1' THEN Col1
WHEN #Col='Col2' THEN Col2
ELSE NULL
END AS MyScalarResultOfDynamicDatatype
FROM ATable
WHERE Idf=#Idf
If it is really, really necessary... you might use the sql_variant data type.
CASE
WHEN #Col='Col1' THEN CAST(Col1 AS SQL_VARIANT)
WHEN #Col='Col2' THEN CAST(Col2 AS SQL_VARIANT)
END AS MyScalarResultOfDynamicDatatype
(Note that you do not specifically need an ELSE NULL in your CASE-statement. If there is no matching WHEN, the result will be NULL by default.)
Edit:
Based on a question in comment regarding the drawbacks, I would like to refer to the article Problems Caused by the Use of the SQL_VARIANT Datatype, written by somebody under the alias Phil Factor (Redgate Simple Talk, Redgate Blog, GitHub).
I have a table T1 with alphanumeric codes (varchar column) where always the three first digits will be numeric like this:
001ABCD
100EFGH
541XYZZ
OTHER
NOTE: Please notice that I have ONE exception record which is all alpha (OTHER).
Also I have a table T2 with 3-digit numbers (int column) like this:
001
200
300
So when I run the following query:
SELECT * from T1
LEFT JOIN T2
ON SUBSTRING(T1.code1,1,3) = T2.code2
WHERE T1.code1 <> 'OTHER'
It is causing me the error:
Conversion failed when converting the varchar value 'OTH' to data type int.
I know the issue but not how to fix it (it's trying to compare 'OTH' with the T2.code2 INT column).
I tried to use WHERE but it didn't work at all.
I cannot get rid of the 'OTHER' record and convert the T2.code2 column from int to varchar is not an option. Any idea?
Here are 3 different ways you can solve this. I would recommended the persisted computed column since it only has to be calculated on insert and update, not every time you run the read query.
DROP TABLE IF EXISTS #T2;
DROP TABLE IF EXISTS #T1;
CREATE TABLE #T1
(
Code1 VARCHAR(10)
,Code2Computed AS TRY_CONVERT(INT,SUBSTRING(Code1,1,3)) PERSISTED
)
;
CREATE TABLE #T2
(
Code2 INT
)
;
INSERT INTO #T1
(Code1)
VALUES
('001ABCD')
,('100EFGH')
,('541XYZZ')
,('OTHER')
;
INSERT INTO #T2
(Code2)
VALUES
(001)
,(100)
,(200)
,(300)
,(541)
;
--Convert INT to 3 digit code
SELECT *
FROM #T1
LEFT JOIN #T2
ON SUBSTRING(#T1.Code1,1,3) = RIGHT(CONCAT('000',#T2.Code2),3)
;
--Convert 3 digit code to INT
SELECT *
FROM #T1
LEFT JOIN #T2
ON TRY_CONVERT(INT,SUBSTRING(#T1.Code1,1,3)) = #T2.Code2
;
--Use computed column
SELECT *
FROM #T1
LEFT JOIN #T2
ON #T1.Code2Computed = #T2.Code2
;
Let's say we have the following table:
CREATE TABLE foo (
column_1 bigint,
column_2 bytea DEFAULT gen_random_bytes(2),
PRIMARY KEY (column_1, column_2)
);
Note: We want column_2 to be random & cryptographically strong.
How do we insert a row without causing a primary key conflict?
I guess we'd have to do a loop until gen_random_bytes(2) returns a unique result? If so, can we do this loop with pure SQL, maybe with recursive CTE, instead of with plpgsql?
insert into t (col1, col2)
select 1, ('\x' || right('000' || to_hex(i), 4))::bytea
from (
select generate_series(0, 65535) i
except
select get_byte(col2, 0) * 256 + get_byte(col2, 1)
from t
where col1 = 1
) s
order by random()
limit 1
I have table source SRC such as:
*Column1*
First
Second
45
Fouth
Now I want to insert these data into table DEST (ID, NAME) with this logic:
If row is numeric, insert into (ID, NAME) VAUES (45, 'TBD').
If the row is not numeric, generate ID and insert into (ID, NAME) VALUES (*GENERATED ID*, NAME).
I tried something like:
DECLARE #i INT;
SELECT #i = MAX ( ID ) + 1
FROM DEST;
IF ( SELECT ISNUMERIC ( SELECT Column1 FROM SRC ) AS help ) = 1
BEGIN
INSERT INTO DEST (ID, NAME) VALUES (45, 'TBD')
END;
ELSE
BEGIN
INSERT INTO DEST (ID, NAME) SELECT ROW_NUMBER() OVER(ORDER BY NAME) +#i, 'First';
INSERT INTO DEST (ID, NAME) SELECT ROW_NUMBER() OVER(ORDER BY NAME) +#i, 'Second';
INSERT INTO DEST (ID, NAME) SELECT ROW_NUMBER() OVER(ORDER BY NAME) +#i, 'Fourth';
END;
(simplified solution to demonstrate the purpose, it should be dynamic, not hardcoded)
.., but that obviously does not work. How to do that?
One approach you can take is the following, which uses a CASE statement to allow you to differentiate between numeric and non-numeric values of Column1:
-- Some temporary tables to make the example work
CREATE TABLE #SRC (Column1 VARCHAR(50))
INSERT INTO #SRC (Column1) VALUES ('First'), ('Second'), ('45'), ('Fourth')
CREATE TABLE #DEST (ID INT)
DECLARE #i INT
-- If #DEST is empty we need to have an initial value of 1
SELECT #i = ISNULL(MAX(ID),0) + 1 FROM #DEST
PRINT #i
INSERT INTO #DEST (ID)
SELECT CASE ISNUMERIC(Column1)
WHEN 1 THEN Column1
ELSE ROW_NUMBER() OVER (ORDER BY Column1) + #i
END
FROM #SRC
SELECT *
FROM #DEST
DROP TABLE #SRC
DROP TABLE #DEST
The example will not port directly into your code, but should give you a good basis to begin working from in order to achieve your desired result.
Note my comment to your original post that you may get clashes on the ID column if inserting multiple rows with this. You will need to consider what to do in that situation.
You could try something like this.
First insert numeric values by checking ISNUMERIC.
INSERT INTO DEST (ID,Name)
SELECT TRY_CONVERT(INT,Column1),'TBD'
FROM SRC
WHERE ISNUMERIC(Column1) = 1
Now Insert other values
DECLARE #maxid INT
SELECT #maxid = MAX(ID) FROM DEST;
INSERT INTO DEST (ID,Name)
SELECT #maxid + ROW_NUMBER()OVER(ORDER BY Column1 ASC),Column1
FROM SRC
WHERE ISNUMERIC(Column1) = 0
Note : ISNUMERIC doesn't guarantee that the value will be converted successfully to a numeric value. Also it looks like you want to check if the value is integer and not numeric unless you are ok with truncation of decimal point. You can use Column1 LIKE '%[0-9]%' to check if a value contains only numbers and not decimal value if that is the case
For Example, the below value '.' returns ISNUMERIC as 1 however cannot be converted to a numeric or an int :
DECLARE #value varchar(10) = '.'
SELECT ISNUMERIC(#value),TRY_CONVERT(INT,#value),TRY_CONVERT(NUMERIC(18,2),#value)
create table test (
col1 varchar(20),
col2 varchar(20)
)
When col1 has value '1', col2 cannot be null.
When col1 has any other value, col2 can be null.
Is there a way to write a check constraints based on values of particular columns?
You can write a table-level constraint, sure.
CREATE TABLE test (
col1 VARCHAR(20),
col2 VARCHAR(20),
CHECK (col1 != '1' OR col2 IS NOT NULL)
);
Either col1 isn't '1' (and col2 can be anything), or col1 is '1' (and col2 can't be null).
See the third example in the manual.