Teradata SUBSTRING Index Out of Bounds - sql

This query works:
SELECT
TOP 100 SUBSTRING(column_name FROM 6 FOR CHARACTER_LENGTH(column_name) - 5) AS X
FROM db_name.table_name
But the following query (with WHERE clause added) does not execute.
SELECT
TOP 100 SUBSTRING(column_name FROM 6 FOR CHARACTER_LENGTH(column_name) - 5) AS X
FROM db_name.table_name
WHERE NOT EXISTS
(
SELECT 1
FROM db_name2.lookup_name H
WHERE H.SRC_NUM1 = X
AND H.SRC_TYPE = 11
)
The query above throws
SELECT Failed. 2663: SUBSTR: string subscript out of bounds in table_name.column_name
However, this following one works (original SELECT is nested)
SELECT *
FROM (
SELECT
TOP 100 SUBSTRING(column_name FROM 6 FOR CHARACTER_LENGTH(column_name) - 5) AS X
FROM db_name.table_name
) A
WHERE NOT EXISTS
(
SELECT 1
FROM db_name2.lookup_name H
WHERE H.SRC_NUM1 = X
AND H.SRC_TYPE = 11
)
Why is that so? I am using SQL assistant to execute the queries but I doubt it is of relevance.

Try to change (maybe error is caused when column_name's lenght is less then 6):
SELECT
TOP 100 CASE WHEN CHARACTER_LENGTH(column_name)>5
THEN SUBSTRING(column_name FROM 6 FOR CHARACTER_LENGTH(column_name) - 5)
ELSE NULL END AS X
FROM db_name.table_name

Related

WITH RECURSIVE: is it possible to COUNT() the working table?

I am using HSQLDB 2.6.1, and I want to COUNT() the working table of a recursive CTE.
I wrote the following test:
with recursive
nums (n, m) as
(
select 1, 1 from (values(1))
union all
select * from (
with
var (k) as
(
select count(*) from nums
)
select n+1, var.k from nums, var where n+1 <= 10
)
)
select n, m from nums;
Here is the result set:
N M
1 1
2 0
3 0
4 0
5 0
6 0
7 0
8 0
9 0
10 0
It seems like COUNT() does not work on the working table.
Was it not supposed to work?
And is there another way to count the working table?
This cannot be done directly in the current version (2.7.0) of HyperSQL.
The column n of your query is incremented in each round, therefore counting the identical n values in the result table gives the size of the delta table for each round.
with recursive nums (n, m) as (
...
) select count(*) from nums group by n;

How to missing numbers by 100s in oracle

I need to find the missing numbers in a table column in oracle, where the missing numbers must be taken by 100s , meaning that if it's found 1 number at least between 2000 and 2099 , all missing numbers between 2000 and 2099 must be returned and so on.
here is an example that clarify what I need:
create table test1 ( a number(9,0));
insert into test1 values (2001);
insert into test1 values (2002);
insert into test1 values (2004);
insert into test1 values (2105);
insert into test1 values (3006);
insert into test1 values (9410);
commit;
the result must be 2000,2003,2005 to 2099,2100 to 2104,2106 to 2199,3000 to 3005,3007 to 3099,9400 to 9409,9411 to 9499.
I started with this query but it's obviously not returning what I need :
SELECT Level+(2000-1) FROM dual CONNECT BY LEVEL <= 9999
MINUS SELECT a FROM test1;
You can use the hiearchy query as follows:
SQL> SELECT A FROM (
2 SELECT A + COLUMN_VALUE - 1 AS A
3 FROM ( SELECT DISTINCT TRUNC(A, - 2) A
4 FROM TEST_TABLE) T
5 CROSS JOIN TABLE ( CAST(MULTISET(
6 SELECT LEVEL FROM DUAL CONNECT BY LEVEL <= 100
7 ) AS SYS.ODCINUMBERLIST) ) LEVELS
8 )
9 MINUS
10 SELECT A FROM TEST_TABLE;
A
----------
2000
2003
2005
2006
2007
2008
2009
.....
.....
I like to use standard recursive queries for this.
with nums (a, max_a) as (
select min(a), max(a) from test1
union all
select a + 1, max_a from nums where a < max_a
)
select n.a
from nums n
where not exists (select 1 from test1 t where t.a = n.a)
order by n.a
The with clause takes the minimum and maximum value of a in the table, and generates all numbers in between. Then, the outer query filters on those that do not exist in the table.
If you want to generate ranges of missing numbers instead of a comprehensive list, you can use window functions instead:
select a + 1 start_a, lead_a - 1 end_a
from (
select a, lead(a) over(order by a) lead_a
from test1
) t
where lead_a <> a + 1
Demo on DB Fiddle
EDIT:
If you want the missing values within ranges of thousands, then we can slightly adapt the recursive solution:
with nums (a, max_a) as (
select distinct floor(a / 100) * 100 a, floor(a / 100) * 100 + 100 from test1
union all
select a + 1, max_a from nums where a < max_a
)
select n.a
from nums n
where not exists (select 1 from test1 t where t.a = n.a)
order by n.a
Assuming you define fixed upper and lower bound for the range, then just need to eliminate the results of the current query by use of NOT EXISTS such as
SQL> exec :min_val:=2000
SQL> exec :min_val:=2499
SQL> SELECT *
FROM
(
SELECT level + :min_val - 1 AS nr
FROM dual
CONNECT BY level <= :max_val - :min_val + 1
)
WHERE NOT EXISTS ( SELECT * FROM test1 WHERE a = nr )
ORDER BY nr;
/
Demo

Integer Value Right Padding in SQL

Step1: I have a table called XYZ which contains following integer columns:
ID A B C
1 201507 20150810 20150311
2 20150812 201509 201510
I need to write a SQL query where if any values of A, B, and C is smaller than 8 digits then I need to consider it as 8 digits by adding zeros to the right of the value for step2 (I am not allowed to update the table.). For example:
ID A B C
1 20150700 20150810 20150311
2 20150812 20150900 20151000
How to add zeros to the right of the integer values through SQL query?
Step 2: I need to find for each record A<B, B<C or not. Please let me know how to write the query. I am using PostgreSQL. Thank you.
SELECT CAST(2015 AS VARCHAR(10))+REPLICATE('0',8-LEN(2015))
SELECT 2015 *(CAST('1'+REPLICATE('0',8-len(2015)) AS INT))
You can use rpad() to add trailing zeros, then cast the result back to an integer:
select id,
rpad(a::text, 8, '0')::int,
rpad(b::text, 8, '0')::int,
rpad(c::text, 8, '0')::int
from the_table;
To avoid repeating the expressions, use a derived table:
select *
from (
select id,
rpad(a::text, 8, '0')::int as a,
rpad(b::text, 8, '0')::int as b,
rpad(c::text, 8, '0')::int as c
from the_table
) t
where a < b or b < c --<< change that to the condition you want
just try this
select
ID,
A = LEFT(cast(a as varchar(100)+'00000000',8),
b = LEFT(cast(b as varchar(100)+'00000000',8),
C = LEFT(cast(c as varchar(100)+'00000000',8)
from xyz
Try this:
select cast(left(cast(A as varchar(20)) + '00000000', 8) as int) as [A],
cast(left(cast(B as varchar(20)) + '00000000', 8) as int) as [B],
cast(left(cast(C as varchar(20)) + '00000000', 8) as int) as [C]
from TABLE_NAME
If you want to avoid any casting, this might be solution:
select case when 8 - LEN(A) > 0 then A * Power(10, (8 - LEN(A))) else A end as [A],
case when 8 - LEN(B) > 0 then B * Power(10, (8 - LEN(B))) else B end as [B],
case when 8 - LEN(C) > 0 then C * Power(10, (8 - LEN(C))) else C end as [C]
from MY_TABLE

SQL - Return all rows for ID where one row meets condition A,B,or C

I'm trying to return all rows for a particular IDs where a condition is met in any one of the rows tied to those IDs. Pardon me being a newbie to SQL... Example below:
ID * Line * # *
12 * 1 * A *
12 * 2 * B *
12 * 3 * X *
12 * 4 * Y *
15 * 1 * A *
15 * 2 * B *
15 * 3 * C *
Not sure what the code would be other than my select and condition = (X, Y, or Z) to return:
ID * Line * # *
12 * 1 * A * <-- doesn't include X, Y, or Z but is part of the ID which
12 * 2 * B * <-- has X in another row of that ID
12 * 3 * X *
12 * 4 * Y *
I'm wanting to pull all row records despite not meeting the condition as long as they're part of the ID that has a row that meets the condition.
Thanks for the help!
* Edit: Including code attempted*
SELECT ID
,LINE
,#
WHERE ID,
IN (
SELECT ID
WHERE # IN ('X','Y','Z'))
Results:
ID LINE #
12 3 X
12 4 Y
What I need:
ID LINE #
12 1 A
12 2 B
12 3 X
12 4 Y
I almost feel like I need to create a temp table of ID & LINE using my condition of IN('X','Y','Z') and then inner join on ID for all LINE(s) not X,Y,Z. I think that may work, but I haven't learned how to use temp tables yet. I'm a little troubled because I'm using a query, which I've simplified a ton here, where I'm selecting 18 fields that join in 7 other tables. I think this is just complicating my understanding of the subquery, not so much the subquery being affected by that.
Thanks all for the help and answers so far!
You can use a subquery and IN for this.
Select *
From YourTable
where ID in (select ID from YourTable where # in ('X','Y','Z'))
Just a note, there is no 12 * 4 * C * in your data but I think it's just a type-o in your results and should be 12 * 4 * Y *
Besides the subquery approach you might also try an OLAP-function (Depending on the actual data this might be better or worse, of course)
In Teradata you can apply QUALIFY:
Select *
From YourTable
qualify -- check if any row with the same ID has X/Y/Z
max(case when ID in ('X','Y','Z') then 1 else 0 end)
over (partition by ID) = 1
In SQL Server you have to use a Derived Table/CTE:
Select *
From
( Select *,
max(case when ID in ('X','Y','Z') then 1 else 0 end)
over (partition by ID) as flag
from YourTable
) as dt
where flag = 1

Using IN with convert in sql

I would like to use the IN clause, but with the convert function.
Basically, I have a table (A) with the column of type int.
But in the other table (B) I Have values which are of type varchar.
Essentially, what I am looking for something like this
select *
from B
where myB_Column IN (select myA_Columng from A)
However, I am not sure if the int from table A, would map / convert / evaluate properly for the varchar in B.
I am using SQL Server 2008.
You can use CASE statement in where clause like this and CAST only if its Integer.
else 0 or NULL depending on your requirements.
SELECT *
FROM B
WHERE CASE ISNUMERIC(myB_Column) WHEN 1 THEN CAST(myB_Column AS INT) ELSE 0 END
IN (SELECT myA_Columng FROM A)
ISNUMERIC will be 1 (true) for Decimal values as-well so ideally you should implement your own IsInteger UDF .To do that look at this question
T-sql - determine if value is integer
Option #1
Select * from B where myB_Column IN
(
Select Cast(myA_Columng As Int) from A Where ISNUMERIC(myA_Columng) = 1
)
Option #2
Select B.* from B
Inner Join
(
Select Cast(myA_Columng As Int) As myA_Columng from A
Where ISNUMERIC(myA_Columng) = 1
) T
On T.myA_Columng = B.myB_Column
Option #3
Select B.* from B
Left Join
(
Select Cast(myA_Columng As Int) As myA_Columng from A
Where ISNUMERIC(myA_Columng) = 1
) T
On T.myA_Columng = B.myB_Column
I will opt third one. Reason is below mentioned.
Disadvantages of IN Predicate
Suppose I have two list objects.
List 1 List 2
1 12
2 7
3 8
4 98
5 9
6 10
7 6
Using Contains, it will search for each List-1 item in List-2 that means iteration will happen 49 times !!!
You can also use exists caluse,
select *
from B
where EXISTS (select 1 from A WHERE CAST(myA_Column AS VARCHAR) = myB_Column)
You can use below query :
select B.*
from B
inner join (Select distinct MyA_Columng from A) AS X ON B.MyB_Column = CAST(x.MyA_Columng as NVARCHAR(50))
Try it by using CAST()
SELECT *
FROM B
WHERE CAST(myB_Column AS INT(11)) IN (
SELECT myA_Columng
FROM A
)