Determine SQL From clause at runtime? - sql

I have a table1 which contains a column where it stores other table's name.
Based on the value in table1, the query should pull out data corresponding to the table name given in it.
For example , let the table which stores tablename be tablelist(tablename,tableid)
Let the other tables whose names stored in tablelist.tablename be A, B , C
Based on a given input parameter tableid,
If the value stored in tablename is 'A' the query should pull out results equivalent to :
Select A.name from A;
If its 'B', the query should be :
Select B.type from B;
If its 'C' , the query should be :
Select C.msg from C;
How will I make it into a single query which accepts the table id as input ?
Please advice

You could try case when construction:
select case tableid
when 'A' then (select name from a)
when 'B' then (select type from b)
when 'C' then (select msg from c)
end
from tbl
Example with some data:
with
tablelist(tablename, tableid) as (
select 'A', 1 from dual union all
select 'B', 2 from dual union all
select 'B', 7 from dual union all
select 'C', 3 from dual ),
a(name) as (select 'Chris' from dual),
b(type) as (select 'T800' from dual),
c(msg) as (select 'Hello' from dual)
select case tablename
when 'A' then (select name from a)
when 'B' then (select type from b)
when 'C' then (select msg from c)
end as value
from tablelist
where tableid = 7
Result T800.

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:

Oracle SQL - Count based on a condition to include distinct rows with zero matches

Is there a "better" way to refactor the query below that returns the number occurrences of a particular value (e.g. 'A') for each distinct id? The challenge seems to be keeping id = 2 in the result set even though the count is zero (id = 2 is never related to 'A'). It has a common table expression, NVL function, in-line view, distinct, and left join. Is all of that really needed to get this job done? (Oracle 19c)
create table T (id, val) as
select 1, 'A' from dual
union all select 1, 'B' from dual
union all select 1, 'A' from dual
union all select 2, 'B' from dual
union all select 2, 'B' from dual
union all select 3, 'A' from dual
;
with C as (select id, val, count(*) cnt from T where val = 'A' group by id, val)
select D.id, nvl(C.cnt, 0) cnt_with_zero from (select distinct id from T) D left join C on D.id = C.id
order by id
;
ID CNT_WITH_ZERO
---------- -------------
1 2
2 0
3 1
A simple way is conditional aggregation:
select id,
sum(case when val = 'A' then 1 else 0 end) as num_As
from t
group by id;
If you have another table with one row per id, you I would recommend:
select i.id,
(select count(*) from t where t.id = i.id and t.val = 'A') as num_As
from ids i;

How to group by and select

I tried to extract customer who has type a
I guess I must group by in customer and tried to having in type
customer type
A a
A c
B b
B c
C a
C a
but I couldn't figure out specific way to achieve this.
If someone has opinion,please let me know.
My desired result is following
customer type
A a
A c
C a
C a
Thanks
Using exists, we can try:
SELECT t1.customer, t1.type
FROM yourTable t1
WHERE EXISTS (SELECT 1 FROM yourTable t2 WHERE t2.customer = t1.customer AND t2.type = 'a');
The exists logic reads in plain English as select any record for which we can find at least one record for the same customer whose type is a. This means retain all customer records, where at least one of those records has type a.
You don't need to group. You can just filter the table for customers that have type 'a' and use that resultset to filter the table again for the customers.
WITH
cust_data
AS
(SELECT 'A' AS customer, 'a' AS TYPE FROM DUAL
UNION ALL
SELECT 'A' AS customer, 'c' AS TYPE FROM DUAL
UNION ALL
SELECT 'B' AS customer, 'b' AS TYPE FROM DUAL
UNION ALL
SELECT 'B' AS customer, 'c' AS TYPE FROM DUAL
UNION ALL
SELECT 'C' AS customer, 'a' AS TYPE FROM DUAL
UNION ALL
SELECT 'C' AS customer, 'a' AS TYPE FROM DUAL)
SELECT *
FROM cust_data c
WHERE customer IN (SELECT customer
FROM cust_data
WHERE TYPE = 'a');
This gives
CUSTOMER TYPE
A c
A a
C a
C a
I hope the below query completes your requirement.
SELECT
*
FROM
test.customer
WHERE
customer IN (SELECT
customer
FROM
test.customer
WHERE
type = 'a');
Output:
A a
A c
C a
C a
Do not use accessing table twice. Use window functions instead.
with t(customer, type) as (
select 'A', 'a' from dual union all
select 'A', 'c' from dual union all
select 'B', 'b' from dual union all
select 'B', 'c' from dual union all
select 'C', 'a' from dual union all
select 'C', 'a' from dual)
select customer, type
from
(select t.*, count(decode(type, 'a', 1)) over (partition by customer) cnt
from t
)
where cnt > 0;
CUSTOMER TYPE
-------- ----
A a
A c
C a
C a

Is this query possible in sqlite?

I have a table like this:
ID A B C
0 x x
1 x
2 x x
3 x
I would like to obtain this with a query (SQLite syntax if possible)
A 3
B 2
C 1
I've been using CASE WHEN clause, like this
WITH solutions AS (SELECT
CASE
WHEN `B` = 'x' THEN 'A'
WHEN `B` = 'x' THEN 'B'
WHEN `C` = 'x' THEN 'C'
END AS 'Solution'
FROM use_cases)
SELECT solution, COUNT(*) AS 'Count'
FROM solutions
GROUP BY Solution ORDER BY COUNT(*) DESC
But it won't work in case a row present more than one column with 'x', as the WHEN evaluation will stop at the first case is found.
Use union all:
select col, count(*)
from (select 'A' as col from t where A = 'x' union all
select 'B' from t where B = 'x' union all
select 'C' from t where C = 'x'
) x
group by col
order by count(*) desc;
You need UNION ALL:
select 'A' col1, count(A = 'x') col2 from tablename
union all
select 'B', count(B = 'x') from tablename
union all
select 'C', count(C = 'x') from tablename
See the demo.
Or with a CTE to avoid multiple scans of the table:
with cte as (
select count(A = 'x') cola, count(B = 'x') colb, count(C = 'x') colc
from tablename
)
select 'A' col1, cola col2 from cte
union all
select 'B', colb from cte
union all
select 'C', colc from cte
See the demo.
Results:
| col1 | col2 |
| ---- | ---- |
| A | 3 |
| B | 2 |
| C | 1 |
If you specifically wanted to check for the X then you could take advantage of the fact that (at least in SQLite) TRUE is equivalent to 1 and FALSE to 0 so something simple like the following would work:
SELECT 'A', sum(A = 'x') from MyTable
UNION ALL
SELECT 'B', sum(B = 'x') from MyTable
UNION ALL
SELECT 'C', sum(C = 'x') from MyTable;
Another simple solution is to rely on the fact that count() only counts non-null values. So if the columns contain NULL in the places where it doesn't contain an X, then this simple SQL will work in SQLite:
SELECT 'A', count(A) from MyTable
UNION ALL
SELECT 'B', count(B) from MyTable
UNION ALL
SELECT 'C', count(C) from MyTable;
A slight variant of the above ... If the columns that don't contain X have something in them other than NULL … then just convert that to null. For example if the columns that don't contain an X happen to contain a space then the following would work:
SELECT 'A', count(nullif (A, ' ')) from MyTable
UNION ALL
SELECT 'B', count(nullif (B, ' ')) from MyTable
UNION ALL
SELECT 'C', count(nullif (C, ' ')) from MyTable;
Note: My proposed approaches will likely require multiple scans of the table but with proper indexing such a penalty could be mitigated. It is possible that CTE-based solutions may be more efficient but you would have to test both approaches to be sure. CTE-based solutions may involve the creation of temp tables (or temp result sets) from potentially multiple subqueries. All of which can make CTEs less efficient in certain circumstances … but YMMV.
Try this
Select val, cnt from ( SELECT 'A' as val,A,count(*) as cnt FROM TABLE
GROUP BY A
UNION
SELECT 'B' as val,B,count(*) as cnt FROM TABLE
Group by B
UNION
SELECT 'C' as val,C,count(*) as cnt FROM TABLE
Group by C))
Check below output
or if you want to exclude empty space count then
SELECT VAL,CNT FROM(SELECT 'A' AS VAL,A as col,COUNT(*) AS CNT FROM SAMPLEE GROUP BY A
UNION
SELECT 'B' AS VAL,B as col,COUNT(*) AS CNT FROM SAMPLEE GROUP BY B
UNION
SELECT 'C' AS VAL,C as col,COUNT(*) AS CNT FROM SAMPLEE GROUP BY C
) where col !=' ';

How to use decode or case function to build a condition base on the result of two records?

Let's say if I search by a key, it returns 2 records with 2 different values for each record.
It will return value 'A' and value 'B' for the 1st and 2nd records respectively.
ID VALUE
1 A
1 B
If the returned records contains 'A' and 'B' then I want to change all their value to 'C'.
If the returned record only contains 'A' or 'B' then i don't want to change to 'C'
How do i use the decode or case function to do that?
I tried (Case when value in('A','B') then 'C' else value end)
but it also changes the records that only returns either 'A' or 'B' to 'C'
So basically if my result are like this :
ID VALUE
1 A
1 B
I want it to be like this
ID VALUE
1 C
1 C
If the result is
ID VALUE or ID VALUE
1 A 1 B
1 A 1 B
Then don't implement the above conversion rule.
Edit for clarity
select id, value from t1
where id =123
gives me below
ID VALUE
1 A
1 B
I want a condition that uses the value of the two records--change the value to 'C' only when clm1.value=A and clm2.value=B
something like below but it does not work.
select id,
case when value ='A' and value ='B' then 'C' else value end
from t1
where id=123
Sorry for the confusion.
Thanks
What about something like this:
create table csm (id int, value varchar(5))
insert into csm (id,value)
SELECT 1,'A' UNION
SELECT 1,'B' UNION
SELECT 2,'A' UNION
SELECT 3,'B' UNION
SELECT 4,'A' UNION
SELECT 4,'B' UNION
SELECT 4,'D'
SELECT t.id
, case when tsub.TotalTimes=2 AND tsub.NumTimes=2 THEN 'C' ELSE t.value END as Value
FROM csm t
JOIN (
SELECT id, COUNT(DISTINCT CASE WHEN value IN ('A','B') THEN value END) AS NumTimes
, COUNT(DISTINCT value) TotalTimes
FROM csm
GROUP BY id
) AS tsub ON t.id=tsub.id
I get the following output:
1 C
1 C
2 A
3 B
4 A
4 B
4 D
The subquery finds out the number of times A and B occur for that id, and then your case statement checks if that value is 2, and if so changes it to C.
Seems like a perfect match for an analytic function:
with v_data(id, value) as (
select 1, 'A' from dual union all
select 1, 'B' from dual union all
select 2, 'A' from dual union all
select 3, 'B' from dual union all
select 3, 'B' from dual
)
select
v1.*,
(case
when v1.cnt_distinct > 1 then 'C'
else v1.value end)
as new_value
from (
select
id,
value,
count(*) over (partition by id) as cnt_overal,
count(distinct value) over (partition by id) as cnt_distinct
from v_data)
v1
This computes the number of distinct values for each ID (using count(distinct...) and then replaces the values with C if the number of distinct values is larger than 1.