Cast varchar that holds some strings to integer field in informix - sql

I have 2 rows from 2 tables in a database that I want to compare.
Column1 is on table1 and is an Integer field with entries like the following
column1
147518
187146
169592
Column2 is on table2 and is a Varchar(15) field with various entries but for this example lets use these 3:
column2
169592
00010000089
DummyId
For my query part of it relies on checking if rows from table1 are linked to the rows in table2, but to do this, I need to compare column1 and column2.
SELECT * FROM table1 WHERE column1 IN (SELECT column2 FROM table2)
The result of this using the data above should be 1 row - 169592
Obviously this wont work (A character to numeric conversion process failed) as they cannot be compared as is, but how do I get them to work?
I have tried
SELECT * FROM table1 WHERE column1 IN (SELECT CAST(column2 AS INTEGER) FROM table2)
and
SELECT * FROM table1 WHERE column1 IN (SELECT (column2::INTEGER) column2 FROM table2)
Using Server Studio 9.1 if that helps.

Try casting the int to a string:
SELECT * FROM table1 WHERE cast(column1 as varchar(15)) IN (SELECT column2 FROM table2)

You can try to use ISNUMERIC in following:
SELECT * FROM table1 WHERE column1 IN (SELECT CASE WHEN ISNUMERIC(column2) = 1 THEN CAST(column2 AS INT) END FROM table2)

For this purpose there is no need to create a special function that you'll not find on other environments.
Let's create a test case for your example:
CREATE TABLE tab1 (
col1 INT,
col2 INT
);
CREATE TABLE tab2 (
col1 VARCHAR(15)
);
INSERT INTO tab1 VALUES(147518,1);
INSERT INTO tab1 VALUES(187146,2);
INSERT INTO tab1 VALUES(169592,3);
INSERT INTO tab2 VALUES(169592);
INSERT INTO tab2 VALUES('00010000089');
INSERT INTO tab2 VALUES('DummyId');
The first query you run was like:
SELECT t1.*
FROM tab1 AS t1
WHERE t1.col1 IN (SELECT t2.col1 FROM tab2 AS t2);
This will raise an error because it tries to compare an INT with a VARCHAR
[infx1210#tardis ~]$ finderr 1213
-1213 A character to numeric conversion process failed.
A character value is being converted to numeric form for storage in a
numeric column or variable. However, the character string cannot be
interpreted as a number. It contains some characters other than white
space, digits, a sign, a decimal, or the letter e; or the parts are in
the wrong order, so the number cannot be deciphered.
If you are using NLS, the decimal character or thousands separator
might be wrong for your locale.
[infx1210#tardis ~]$
Then you've tried to cast a VARCHAR into a INT which resulted in the same error, you should tried the other way:
> SELECT t1.*
> FROM tab1 AS t1
> WHERE t1.col1::CHAR(11) IN (SELECT t2.col1 FROM tab2 AS t2);
>
col1 col2
169592 3
1 row(s) retrieved.
>
Check also if you don't get faster results using the EXISTS:
> SELECT t1.*
> FROM tab1 AS t1
> WHERE EXISTS (
> SELECT 1
> FROM tab2 AS t2
> WHERE t1.col1::CHAR(11) = t2.col1
> );
col1 col2
169592 3
1 row(s) retrieved.
>
Another way possible is to just join the tables:
> SELECT t1.*
> FROM tab1 AS t1
> INNER JOIN tab2 AS t2
> ON (t1.col1 = t2.col1);
col1 col2
169592 3
1 row(s) retrieved.
>

Part of this question was answered by #Stanislovas Kalašnikovas where he said to use the following:
SELECT * FROM table1 WHERE column1 IN (SELECT CASE WHEN ISNUMERIC(column2) = 1 THEN CAST(column2 AS INT) END FROM table2)
But informix does not have a built in function for ISNUMERIC, so the following created it:
create function isnumeric2(inputstr varchar(15)) returning integer;
define numeric_var decimal(15,0);
define function_rtn integer;
on exception in (-1213)
let function_rtn = 0;
end exception with resume
let function_rtn = 1;
let numeric_var = inputstr;
return function_rtn;
end function;
And then the first query above worked for me.

Related

finding records which don't have corresponding records with an extension at the end

Id like to find records with (.XX) extension at the end which don't have corresponding records without an extension (.XX) at the end. Id like to use the "exists" or "not exists" solution if possible as I'm puzzled why mine gives no output.
input
col_a
value1.XX
value1
value2.XX
value3
** expected output**
col_a
value2.XX
code
SELECT *
FROM table1 as a
where
right (table1.[col_a],3) = ".XX"
and exists(
select 1 from table1 b where Left(a.[col_a], Len(a.[col_a]) - 3) = b.[col_a]
)
Hmmm. You
select t1.*
from table1 t1
where t1.col_a like '%.XX' and
not exists (select 1
from table1 tt1
where t1.col_a = tt1.col_a || '.XX'
);
Note: You have not specified your database so this uses the ISO/ANSI standard || for string concatenation. You can check your RDBMs's documentation for the correct concatenation technique.

How to select a value from different row if the column is null in the current row?

I have a decode statement in my select SQL like this -
...
decode(instr(col1,'str1'), 0, 'STR1', 'STR2') as NAME,
...
The problem is the col1 could be null. So I thought I could use an inner decode like the following -
decode(instr(
decode(col1, null, (
select unique col1 from SAMETABLE st where st.pid = pid) as col2, col1), 'str1'), 0, 'STR1', 'STR2') as NAME,
But it failed.
Here is a possible snapshot of what in DB -
col1 pid
row1 null 1
row2 somevalue 1
I would like to use the value of col1 in row2 to replace the value in row1 when col1 is null in row1 and the two records' pid are equal.
Can anyone point out if I'm doing something impossible?
There are the following issues with your code:
You give the inner table an alias st and then do where st.pid = pid, but that is a self-reference, because also the other pid is taken from the table of the inner query. Instead, give the table in the main query an alias.
You give the outcome of the inner query an alias (as col2), but giving aliases is not allowed inside expressions, so that needs to be removed.
The inner query selects unique col1, but that can still give multiple results, which will give an error. The inner query must return exactly one value at all times (when there are different non null values, and even when there are none). So you should use an aggregate function, like min
decode(a, null, b, a) is a long way to write nvl(a, b)
So you could use this:
select decode(
instr(
nvl(col1, (select min(col1) from t where pid = t1.pid)),
'str1'
),
0, 'STR1', 'STR2'
) as NAME
from mytable t1
I have tried this in Oracle 11 g and it works pretty well. I have also tried to change the starting value of col1 and it works. So i guess you have some other issues that is related to the field type not on how DECODE works.
DECLARE
col1 VARCHAR(10);
result VARCHAR2(10);
BEGIN
col1:=null;
select DECODE(
instr(DECODE(col1, null, (select 'HELLO' from DUAL),
col1),'str1'), 0, 'STR1', 'STR2') into result
from DUAL;
dbms_output.PUT_LINE(result);
END
I guess you have to change the subquery :
select unique col1 from SAMETABLE st where st.pid = pid
with something like
select unique col1 from SAMETABLE st where st.pid = pid and col1 is not null

Calculate multiple columns with each other using CTE

I want to build columns that calculated with each other. (Excuse my English)
Example:
Id Column1 Column2 Column3
1 5 5 => Same as Column1 5 => Same as Column2
2 2 12 => column1 current + column2.prev + column3.previous = 2+5+5 17 => column2.current + column3.prev = 12+5
3 3 32 => 3+12+17 49 => 32+17
easier way to see:
Id Column1 Column2 Column3
1 5 5 => Same as Column1 5 => Same as Column2
2 2 12 => 2+5+5 17 => 12+5
3 3 32 => 3+12+17 49 => 32+17
so complicated??? :-(
The previous issue was calculating Column3 with the new calculated column as Column2. But now, it must be renew with the just calculated Column2 and the previous record of Column3 as well. If you want to have a look at the previous post, here it is.
Here is my previous recursive CTE code. It works like, 1st, calculate column2 with previous record of current column (c.Column2) in cteCalculation, and then calculate new column3 in cte2 with just calculated column2 from cteCalculation.
/copied from that previous post/
;with cteCalculation as (
select t.Id, t.Column1, t.Column1 as Column2
from table_1 t
where t.Id = 1
union all
select t.Id, t.Column1, (t.Column1 + c.Column2) as Column2
from table_1 t
inner join cteCalculation c
on t.Id-1 = c.id
),
cte2 as(
select t.Id, t.Column1 as Column3
from table_1 t
where t.Id = 1
union all
select t.Id, (select column2+1 from cteCalculation c where c.id = t.id) as Column3
from table_1 t
inner join cte2 c2
on t.Id-1 = c2.id
)
select c.Id, c.Column1, c.Column2, c2.column3
from cteCalculation c
inner join cte2 c2 on c.id = c2. id
Now I wanna extend it like calculate 2 columns with the data from each other. Means, use 2nd to calc the 3rd, and use 3rd to get new 2nd column data. Hope you can get it.
This is an example how to achive this using recursive CTE
create table #tmp (id int identity (1,1), Column1 int)
insert into #tmp values(5)
insert into #tmp values(2)
insert into #tmp values(3);
with counter as
(
SELECT top 1 id, Column1, Column1 as Column2, Column1 as Column3 from #tmp
UNION ALL
SELECT t.id, t.Column1,
t.Column1 + counter.Column2 + counter.Column3,
(t.Column1 + counter.Column2 + counter.Column3) + counter.Column3 FROM counter
INNER JOIN #tmp t ON t.id = counter.id + 1
)
select * from counter
You'll need to use a Recursive CTE since the values of subsequent columns are dependent upon earlier results.
Do this in pieces, too. Have your first query just return the correct values for Column1. Your next (recursive CTE) query will add the results for Column2, and so on.
OK I'm assuming you're doing inserts into column 1 here of various values.
Essentially col2 always = new col1 value + old col2 value + old col 3 value
col3 = new col2 value + old col3 value
so col3 = (new col1 value + old col2 value + old col 3 value) + old col3 value
So an INSTEAD OF Insert trigger is probably the easiest way to implement.
CREATE TRIGGER tr_xxxxx ON Tablename
INSTEAD OF INSERT
AS
INSERT INTO Tablename (Column1, Column2, Column3)
SELECT ins.col1, ins.col1+t.col2+t.col3, ins.col1+t.col2+t.col3+t.col3
FROM Tablename t INNER JOIN Inserted ins on t.Id = ins.Id
The trigger has access to both the existing (old) values in Tablename t, and the new value being inserted (Inserted.col1).

Detect (find) string in another string ( nvarchar (MAX) )

I've got nvarchar(max) column with different values alike 'A2'
And another column from another table with values alike '(A2 AND A3) OR A4'
I need to detect does string from second column contains string from first column.
So then I need to select all columns of second table which contains an string from first column of first table.
something alike ... but that is wrong
SELECT * Cols FROM T2
WHERE (SELECT T1.StringCol FROM T1) IN T2.StringCol
but I more understand it like it (in f# syntax)
for t1.date, t1.StringCol from t1
for t2.StringCol from t2
if t2.StringCol.Contains( t1.StringCol )
yield t2.StringCol, t1.date
This should get what you want...
select t2.*
from t1 cross join t2
where patindex('%' + t1.StringCol + '%', t2.StringCol) > 0

How do I compare two columns for equality in SQL Server?

I have two columns that are joined together on certain criteria, but I would also like to check if two other columns are identical and then return a bit field if they are.
Is there a simpler solution than using CASE WHEN?
Ideally I could just use:
SELECT Column1 = Column2 AS MyDesiredResult
FROM Table1
INNER JOIN Table2 ON Table1.PrimaryKey = Table2.ForeignKey
What's wrong with CASE for this? In order to see the result, you'll need at least a byte, and that's what you get with a single character.
CASE WHEN COLUMN1 = COLUMN2 THEN '1' ELSE '0' END AS MyDesiredResult
should work fine, and for all intents and purposes accomplishes the same thing as using a bit field.
CASE WHEN is the better option
SELECT
CASE WHEN COLUMN1 = COLUMN2
THEN '1'
ELSE '0'
END
AS MyDesiredResult
FROM Table1
INNER JOIN Table2 ON Table1.PrimaryKey = Table2.ForeignKey
The use of IIF? And it depends on version of SQL Server.
SELECT
IIF(Column1 = Column2, 1, 0) AS MyDesiredResult
FROM Table;
I'd go with the CASE WHEN also.
Depending on what you actually want to do, there may be other options though, like using an outer join or whatever, but that doesn't seem to be what you need in this case.
Regarding David Elizondo's answer, this can give false positives. It also does not give zeroes where the values don't match.
Code
DECLARE #t1 TABLE (
ColID int IDENTITY,
Col2 int
)
DECLARE #t2 TABLE (
ColID int IDENTITY,
Col2 int
)
INSERT INTO #t1 (Col2) VALUES (123)
INSERT INTO #t1 (Col2) VALUES (234)
INSERT INTO #t1 (Col2) VALUES (456)
INSERT INTO #t1 (Col2) VALUES (1)
INSERT INTO #t2 (Col2) VALUES (123)
INSERT INTO #t2 (Col2) VALUES (345)
INSERT INTO #t2 (Col2) VALUES (456)
INSERT INTO #t2 (Col2) VALUES (2)
SELECT
t1.Col2 AS t1Col2,
t2.Col2 AS t2Col2,
ISNULL(NULLIF(t1.Col2, t2.Col2), 1) AS MyDesiredResult
FROM #t1 AS t1
JOIN #t2 AS t2 ON t1.ColID = t2.ColID
Results
t1Col2 t2Col2 MyDesiredResult
----------- ----------- ---------------
123 123 1
234 345 234 <- Not a zero
456 456 1
1 2 1 <- Not a match
A solution avoiding CASE WHEN is to use COALESCE.
SELECT
t1.Col2 AS t1Col2,
t2.Col2 AS t2Col2,
COALESCE(NULLIF(t1.Col2, t2.Col2),NULLIF(t2.Col2, t1.Col2)) as NULL_IF_SAME
FROM #t1 AS t1
JOIN #t2 AS t2 ON t1.ColID = t2.ColID
NULL_IF_SAME column will give NULL for all rows where t1.col2 = t2.col2 (including NULL).
Though this is not more readable than CASE WHEN expression, it is ANSI SQL.
Just for the sake of fun, if one wants to have boolean bit values of 0 and 1 (though it is not very readable, hence not recommended), one can use (which works for all datatypes):
1/ISNULL(LEN(COALESCE(NULLIF(t1.Col2, t2.Col2),NULLIF(t2.Col2, t1.Col2)))+2,1) as BOOL_BIT_SAME.
Now if you have one of the numeric data types and want bits, in the above LEN function converts to string first which may be problematic,so instead this should work:
1/(CAST(ISNULL(ABS(COALESCE(NULLIF(t1.Col2, t2.Col2),NULLIF(t2.Col2, t1.Col2)))+1,0)as bit)+1) as FAST_BOOL_BIT_SAME_NUMERIC
Above will work for Integers without CAST.
NOTE: also in SQLServer 2012, we have IIF function.
The closest approach I can think of is NULLIF:
SELECT
ISNULL(NULLIF(O.ShipName, C.CompanyName), 1),
O.ShipName,
C.CompanyName,
O.OrderId
FROM [Northwind].[dbo].[Orders] O
INNER JOIN [Northwind].[dbo].[Customers] C
ON C.CustomerId = O.CustomerId
GO
NULLIF returns the first expression if the two expressions are not equal. If the expressions are equal, NULLIF returns a null value of the type of the first expression.
So, above query will return 1 for records in which that columns are equal, the first expression otherwise.