Can I have dynamic FROM Clause in SQL? - sql

I'm currently working with my report parameter list of value that is dependent in another parameter.
I have come up with this idea, is there any possible way to for this to work?
WITH A AS (
SELECT DISTINCT columnA1 FROM Table1
UNION SELECT DISTINCT columnA2 FROM Table1
UNION SELECT DISTINCT columnA3 FROM Table1)
WITH B AS (SELECT DISTINCT columnB1 FROM Table1
UNION SELECT DISTINCT columnB2 FROM Table1
UNION SELECT DISTINCT columnB3 FROM Table1)
Select * from CASE WHEN (:PM_Parameter1 = 'A')
THEN A
ELSE B
END;

Assuming this is Oracle SQL, you can use a the EXISTS function to check for the parameter value, then combine the sets using UNION.
Try playing with this SQL:
select * from
(
select 'A' from dual
union
select 'B' from dual
)
where exists
(SELECT 'Y'
FROM dual
where 'parameter' = 'parameter'
)
union
select * from
(
select 'X' from dual
union
select 'Y' from dual
)
where exists
(SELECT 'Y'
FROM dual
where 'parameter' != 'parameter'
)
If you reverse both the conditions 'parameter' = 'parameter' and 'parameter' != 'parameter', it will return two different row sets.
I am sure this can be optimized again, hope it works out for you.

Related

Count distinct letters in a string in bigquery

I have a string column in Biquery like:
select 'A'
union all (select 'ab')
union all (select 'abc')
union all (select 'aa')
union all (select 'aab')
I would like to count the number of distinct characters in every row of the column, in this case the results would be:
1
2
3
1
2
Can this be done in BigQuery? How?
How about this (assuming you don't want to differentiate between uppercase and lowercase)...
WITH data AS (select 'A' AS `val`
union all (select 'ab')
union all (select 'abc')
union all (select 'aa')
union all (select 'aab'))
SELECT `val`, 26 - LENGTH(REGEXP_REPLACE('abcdefghijklmnopqrstuvwxyz', '['||LOWER(`val`)||']', ''))
FROM `data`;
A simple approach is to use the SPLIT to convert your string to an array and UNNEST to convert the resulting array to a table. You may then use COUNT and DISTINCT to determine the number of unique characters as shown below:
with my_data AS (
select 'A' as col
union all (select 'ab')
union all (select 'abc')
union all (select 'aa')
union all (select 'aab')
)
select col, (SELECT COUNT(*) FROM (
SELECT DISTINCT element FROM UNNEST(SPLIT(col,'')) as element
)) n from my_data;
or simply
WITH my_data AS (
SELECT 'A' as col UNION ALL
SELECT 'ab' UNION ALL
SELECT 'abc' UNION ALL
SELECT 'aa' UNION ALL
SELECT 'aab'
)
SELECT
col,
(
SELECT
COUNT(DISTINCT element)
FROM
UNNEST(SPLIT(col,'')) as element
) cnt
FROM
my_data;
Like previous but using COUNT with DISTINCT
with my_data AS (
select 'A' as col
union all (select 'ab')
union all (select 'abc')
union all (select 'aa')
union all (select 'aab')
)
select col, COUNT(DISTINCT element) FROM
my_data,UNNEST(SPLIT(col,'')) as element
GROUP BY col
If the data is not quite huge, I would rather go with the user-defined functions to ease up the string manipulation across different columns
CREATE TEMP FUNCTION
get_unique_char_count(x STRING)
RETURNS INT64
LANGUAGE js AS r"""
str_split = new Set(x.split(""));
return str_split.size;
""";
WITH
result AS (
SELECT
'A' AS val
UNION ALL (
SELECT
'ab')
UNION ALL (
SELECT
'abc')
UNION ALL (
SELECT
'aa')
UNION ALL (
SELECT
'aab') )
SELECT
val,
get_unique_char_count(val) unique_char_count
FROM
result
RESULT:

sql how to return a list in a condition

I am new with SQL and I would like to ask for your help concerning a problem that I am facing
the first query query1 that can return 0 or 1 line:
with query1 as (select ... from table) select * from query1
returns
query1
-
or
query1
4567
also a second request returning a list:
with my_list as (select 'a' from dual union all select 'b' from dual) select * from my_list
returns
my_list
a
b
my problem is the following: I can not condition my first query so that in case query1 returns null then I display my_list.
I tried with this :
with my_list as (select 'a' from dual union all select 'b' from dual)
select case when ( ( select '' from dual /* case when query1 returns nothing */ ) is null )
then (select 'a' from dual union all select 'b' from dual) /* my_list */
else (select '' from dual) end
from my_list;
but i have the following error :
ORA-01427: single-row subquery returns more than one row
what is this error?
what i want to do is that :
with my_list as (select 'a' from dual union all select 'b' from dual),
myquery as (select '' from dual)
select case when ( myquery is null )
then (select my_list)
else (select myquery end)
so that this query have to return
a
b
thanks for your help
I think you want union all:
with query1 as (select ... from table) select * from query1,
my_list as ( . . . )
select q.*
from query1 q
where ? is not null
union all
select ml.*
from my_list ml
where not exists (select 1 from query1 where ? is not null);
? is for the column returned by query1.
thanks for your help Gordon but I run this query
with query1 as (select '' from dual), my_list as (select 'a' from dual union all select 'b' from dual)
select q.* from query1 q
union select ml.* from my_list ml
where not exists (select 1 from query1);
it returns null value in stead of returning my_list

Why do I still get duplicates on [CONMAT_MATCHING_DONOR] even after using DISTINCT?

I still get duplicates on [CONMAT_MATCHING_DONOR] even after using DISTINCT.
SELECT TOP 1000 [CONTRIB_MATCH_ID]
,[CONMAT_CONTRIBUTION]
,[CONMAT_FORM_RECEIVED_DATE]
,[CONMAT_MATCHING_DONOR]
,[CONMAT_STATUS]
,[STATUS_DESC]
,[CONMAT_STATUS_DATE]
FROM [ods_production].[dbo].[SPT_CONTRIB_MATCH]
WHERE [CONMAT_MATCHING_DONOR] IN (SELECT DISTINCT
[CONMAT_MATCHING_DONOR]
FROM [ods_production].[dbo].[SPT_CONTRIB_MATCH])
ORDER BY [CONMAT_MATCHING_DONOR] DESC
Your usage of DISTINCT in the IN clause doesn't make much sense - this will not affect the results of your query in any way.
Consider:
WITH v_base(name) AS (
SELECT 'A' UNION ALL
SELECT 'A' UNION ALL
SELECT 'B')
SELECT name FROM v_base WHERE name IN (SELECT DISTINCT name from v_base)
which more or less translates to
WITH v_base(name) AS (
SELECT 'A' UNION ALL
SELECT 'A' UNION ALL
SELECT 'B')
SELECT name FROM v_base WHERE name IN ('A', 'B')
vs
WITH v_base(name) AS (
SELECT 'A' UNION ALL
SELECT 'A' UNION ALL
SELECT 'B')
SELECT DISTINCT name FROM v_base WHERE name IN (SELECT name from v_base)
which translates to
WITH v_base(name) AS (
SELECT 'A' UNION ALL
SELECT 'A' UNION ALL
SELECT 'B')
SELECT DISTINCT name FROM v_base WHERE name IN ('A', 'A', 'B')
and the difference should become clear.
SQL Fiddle

How to use "IN" operator with several columns?

Is it possible this query:
select *
from table t
where (t.key1, t.key2, t.key3) IN ('a','b','c','d')
Condition with several columns and several values.
You have to split it in multiple statements. I didn't know whether you want to OR or AND, so I took AND in this samples:
select *
from table t
where (t.key1) IN ('a','b','c','d')
and (t.key2) IN ('a','b','c','d')
and (t.key3) IN ('a','b','c','d')
Or use a join and inline view:
select *
from table t
join ( select 'a' col
from dual
union
select 'b'
from dual
union
select 'c'
from dual
union
select 'd'
from dual
) x
on t.key1 = x.col
and t.key2 = x.col
and t.key3 = x.col
select *
from table t
where t.key1 IN ('a','b','c','d') OR
t.key2 IN ('a','b','c','d') OR
t.key3 IN ('a','b','c','d')

cross-dbms way to check if string is numeric

Ok, I have this field: code varchar(255). It contains some values used in our export routine like
DB84
DB34
3567
3568
I need to select only auto-generated (fully numeric) fields
WHERE is_numeric(table.code)
is_numeric() checks if code field contains only positive digits.
Can you propose anything that will work both under mysql 5.1 and oracle 10g?
Below are three separate implementations for each of SQL Server, MySQL and Oracle. None use (or can) the same approach, so there doesn't seem to be a cross DBMS way to do it.
For MySQL and Oracle, only the simple integer test is show; for SQL Server, the full numeric test is shown.
For SQL Server:
note that isnumeric('.') returns 1.. but it can not actually be converted to float. Some text like '1e6' cannot be converted to numeric directly, but you can pass through float, then numeric.
;with tmp(x) as (
select 'db01' union all select '1' union all select '1e2' union all
select '1234' union all select '' union all select null union all
select '1.2e4' union all select '1.e10' union all select '0' union all
select '1.2e+4' union all select '1.e-10' union all select '1e--5' union all
select '.' union all select '.123' union all select '1.1.23' union all
select '-.123' union all select '-1.123' union all select '--1' union all
select '---1.1' union all select '+1.123' union all select '++3' union all
select '-+1.123' union all select '1 1' union all select '1e1.3' union all
select '1.234' union all select 'e4' union all select '+.123' union all
select '1-' union all select '-3e-4' union all select '+3e-4' union all
select '+3e+4' union all select '-3.2e+4' union all select '1e1e1' union all
select '-1e-1-1')
select x, isnumeric(x),
case when x not like '%[^0-9]%' and x >'' then convert(int, x) end as SimpleInt,
case
when x is null or x = '' then null -- blanks
when x like '%[^0-9e.+-]%' then null -- non valid char found
when x like 'e%' or x like '%e%[e.]%' then null -- e cannot be first, and cannot be followed by e/.
when x like '%e%_%[+-]%' then null -- nothing must come between e and +/-
when x='.' or x like '%.%.%' then null -- no more than one decimal, and not the decimal alone
when x like '%[^e][+-]%' then null -- no more than one of either +/-, and it must be at the start
when x like '%[+-]%[+-]%' and not x like '%[+-]%e[+-]%' then null
else convert(float,x)
end
from tmp order by 2, 3
For MySQL
create table tmp(x varchar(100));
insert into tmp
select 'db01' union all select '1' union all select '1e2' union all
select '1234' union all select '' union all select null union all
select '1.2e4' union all select '1.e10' union all select '0' union all
select '1.2e+4' union all select '1.e-10' union all select '1e--5' union all
select '.' union all select '.123' union all select '1.1.23' union all
select '-.123' union all select '-1.123' union all select '--1' union all
select '---1.1' union all select '+1.123' union all select '++3' union all
select '-+1.123' union all select '1 1' union all select '1e1.3' union all
select '1.234' union all select 'e4' union all select '+.123' union all
select '1-' union all select '-3e-4' union all select '+3e-4' union all
select '+3e+4' union all select '-3.2e+4' union all select '1e1e1' union all
select '-1e-1-1';
select x,
case when x not regexp('[^0-9]') then x*1 end as SimpleInt
from tmp order by 2
For Oracle
case when REGEXP_LIKE(col, '[^0-9]') then col*1 end