How to chose Table based on parameterized database name? - sql

My code takes in a parameter ${ID}$ (string) and based on what ID evaluates to I want to chose a different table to use. Guess I cant use a case inside a FROM statement. Some example code looks like:
select *
from ${ID}$_charges.transaction_charge
where execution_date = '2011-03-22'
So if ID is 'N' then I want to use the transaction_charge table so the statement resolves to N_charges.transaction_charge
However if ID is 'B' or 'P' then I want to use a different table called conformity_charge and the statement would evaluate to B_charges.conformity_charge or P_charges.conformity_charge
How can I write this statement?

If you have a low number of possible tables to target, the closest you can get, apart from dynamic SQL, is:
NOTE: Depending of the capabilities of your database engine and the size of your tables there might be performance penalties that may or may not matter.
SELECT a, b, c
FROM (
SELECT 'N' as TableName, a, b, c
FROM N_charges.transaction_charge
UNION ALL
SELECT 'P' as TableName, a, b, c
FROM P_charges.transaction_charge
UNION ALL
SELECT 'B' as TableName, a, b, c
FROM B_charges.transaction_charge
) t
WHERE TableName = '${ID}$'
# Another variation
SELECT a, b, c
FROM N_charges.transaction_charge
WHERE 'N' = '${ID}$'
UNION ALL
SELECT a, b, c
FROM P_charges.transaction_charge
WHERE 'P' = '${ID}$'
UNION ALL
SELECT a, b, c
FROM B_charges.transaction_charge
WHERE 'B' = '${ID}$'

Related

Oracle SQL select query that return results with diacritis inside the name

Hello I have a Java app that runs an Oracle SQL select query that returns some results.
This is the select:
select a,b,c where a='%ruicanescu%'
This returns only results where the name is exactly 'ruicanescu' but I would like also to return for example: 'ruicănescu' and other diacritics inside the name. Can someone give me an example ? Thanks
You could use CONVERT:
SELECT a, b, c
FROM table_name
WHERE CONVERT(a, 'US7ASCII') LIKE '%ruicanescu%';
Or you can set the NLS_SORT and NLS_COMP session parameters to accent insensitive matching:
ALTER SESSION SET NLS_COMP='LINGUISTIC';
ALTER SESSION SET NLS_SORT='BINARY_AI';
SELECT a, b, c
FROM table_name
WHERE a LIKE '%ruicanescu%';
Which, for the sample data:
CREATE TABLE table_name (a, b, c) AS
SELECT 'ruicănescu', 1, 1 FROM DUAL UNION ALL
SELECT 'ruicanescu', 2, 2 FROM DUAL;
Both output:
A
B
C
ruicănescu
1
1
ruicanescu
2
2
db<>fiddle here

How to update a column for all rows after each time one row is processed by a UDF in BigQuery?

I'm trying to update a column for all rows after each time one row is processed by a UDF.
The example has 3 rows with 6 columns. Column "A" has the same value across 3 rows; column "B" and "A" is the joint identifier of each row; column "C" is arrays with any letters in a,b,c,d,e; column "D" is the target array to be filled in; column "E" is some integers; column "abcde" is the integer array with 5 integers specifying the counts for each letter a,b,c,d,e.
Each row will be passed into a UDF to update the column "D" and column "abcde" according to the column "C" and column "E". The rule is: select the number, which specified by "E", of items from "C" to put into "D"; the selection is random; after each selection done for a row, the column 'abcde' will be updated across all rows.
For example, to process the first row, we randomly select one item from ('a','b','c') to put into "D". Let's say the system picked the 'c' in the column "C", so the value in "D" for this row becomes ['c'] and 'abcde' gets updated to [1,3,1,1,1] (before was [1,3,2,1,1]) for all three rows.
Example data:
#StandardSQL in BigQuery
#code to generate the example table
with sample as (
select 'y1' as A, 'x1' as B, ['a','b','c'] as C, [] as D, 1 as E, [1,3,2,1,1] as abcde union all
select 'y1','x2',['a','b'],[],2,[1,3,2,1,1] union all
select 'y1','x3',['c','d','e'],[],3,[1,3,2,1,1])
select * from sample order by B
After the first row is processed:
with sample as (
select 'y1' as A, 'x1' as B, ['a','b','c'] as C, ['c'] as D, 1 as E, [1,3,1,1,1] as abcde union all
select 'y1','x2',['a','b'],[],2,[1,3,1,1,1] union all
select 'y1','x3',['c','d','e'],[],3,[1,3,1,1,1])
select * from sample order by B
After the second row is processed:
with sample as (
select 'y1' as A, 'x1' as B, ['a','b','c'] as C, ['c'] as D, 1 as E, [0,2,1,1,1] as abcde union all
select 'y1','x2',['a','b'],['a','b'],2,[0,2,1,1,1] union all
select 'y1','x3',['c','d','e'],[],3,[0,2,1,1,1])
select * from sample order by B
After the third row is processed:
with sample as (
select 'y1' as A, 'x1' as B, ['a','b','c'] as C, ['c'] as D, 1 as E, [0,2,0,0,0] as abcde union all
select 'y1','x2',['a','b'],['a','b'],2,[0,2,0,0,0] union all
select 'y1','x3',['c','d','e'],['c','d','e'],3,[0,2,0,0,0])
select * from sample order by B
Don't worry about how the UDF will do the random selection. I'm just wondering, if it's possible in BigQuery to do the task to update the column 'abcde' in the way I want?
I've tried using UDFs, but I'm struggling to get it working because my understanding of a UDF is that it can only take one row in and produce multiple rows out. So, I can't update the other rows. Is it possible just using SQL?
Expected output:
After the first row is processed:
After the third row is processed:
Additional information:
create temporary function selection(A string, B string, C ARRAY<STRING>, D ARRAY<STRING>, E INT64, abcde ARRAY<INT64>)
returns STRUCT< A stRING, B string, C array<string>, D array<string>, E int64, abcde array<int64>>
language js AS """
/*
for the row i in the data:
select the number i.E of items (randomly) from i.C where the numbers associated with the item in i.abcde is bigger than 0 (i.e. only the items with numbers in abcde bigger than 0 can be the cadidates for the random selection);
put the selected items in i.D and deduct the amount of selected items from the number for the corresponding item in the column 'abcde' FOR ALL ROWS;
proceed to the next row i+1 until every row is processed;
*/
return {A,B,C,D,E,abcde}
""";
with sample as (
select 'y1' as A, 'x1' as B, ['a','b','c'] as C, CAST([] AS ARRAY<STRING>) as D, 1 as E, [1,3,2,1,1] as abcde union all
select 'y1','x2',['a','b'],[],2,[1,3,2,1,1] union all
select 'y1','x3',['c','d','e'],[],2,[1,3,2,1,1])
select selection(A,B,C,D,E,abcde) from sample order by B
Below is for BigQuery Standard SQL
#StandardSQL
WITH sample AS (
SELECT 'y1' AS A, 'x1' AS B, ['a','b','c'] AS C, ['c'] AS D, 1 AS E, [1,3,2,1,1] AS abcde UNION ALL
SELECT 'y1','x2',['a','b'],['a','b'],2,[1,3,2,1,1] UNION ALL
SELECT 'y1','x3',['c','d','e'],['c','d','e'],3,[1,3,2,1,1] UNION ALL
SELECT 'y2' AS A, 'x1' AS B, ['a','b','c'] AS C, ['a','b'] AS D, 2 AS E, [1,3,2,1,1] AS abcde UNION ALL
SELECT 'y2','x2',['a','b'],['b'],1,[1,3,2,1,1] UNION ALL
SELECT 'y2','x3',['c','d','e'],['d','e'],2,[1,3,2,1,1]
),
counts AS (
SELECT A AS AA, dd, COUNT(1) AS cnt
FROM sample, UNNEST(D) AS dd
GROUP BY AA, dd
),
processed AS (
SELECT A, B, ARRAY_AGG(aa - IFNULL(cnt, 0) ORDER BY pos) AS abcde
FROM sample, UNNEST(abcde) AS aa WITH OFFSET AS pos
LEFT JOIN counts ON A = counts.AA
AND CASE dd
WHEN 'a' THEN 0
WHEN 'b' THEN 1
WHEN 'c' THEN 2
WHEN 'd' THEN 3
WHEN 'e' THEN 4
END = pos
GROUP BY A, B
)
SELECT s.A, s.B, s.C, s.D, s.E, p.abcde
FROM sample AS s
JOIN processed AS p
USING (A, B)
-- ORDER BY A, B
Don't worry about how the UDF will do the random selection
So, as you can see - I just put "random" values into sample data to mimic D

Reducing the length of my SQL code

I am using a third party software for this SQL query which has various limitations, one being a character limit of 1,000 characters. I have created code that will pull through a list of contact numbers registered to a particular account, and have had a view created specifically for this task. My current script (see below) works, but brings back duplicate responses. I want to shorten the length of the code, and to select for only Distinct responses.
As background, the CRM system this is linked to has multiple places someone can leave a contact number, so I need the code to remove any duplicates when it displays the response. The current code does not attempt to remove duplicates due to the aforementioned character limit, it is:
SELECT [Contact_Mobile_Phone] AS V, [Contact_Mobile_Phone] AS D
FROM [DatabaseName]
WHERE MAINLKUPID = '{Import.TenantID}' AND [Contact_Mobile_Phone] IS NOT NULL
UNION ALL
SELECT [Contact_Home_Phone] AS V, [Contact_Home_Phone] AS D
FROM [DatabaseName]
WHERE MAINLKUPID = '{Import.TenantID}' AND [Contact_Home_Phone] IS NOT NULL
UNION ALL
SELECT [Contact_Work_Phone] AS V, [Contact_Work_Phone] AS D
FROM [DatabaseName]
WHERE MAINLKUPID = '{Import.TenantID}' AND [Contact_Work_Phone] IS NOT NULL
UNION ALL
SELECT [Group_Home_Phone] AS V, [Group_Home_Phone] AS D
FROM [DatabaseName]
WHERE MAINLKUPID = '{Import.TenantID}' AND [Group_Home_Phone] IS NOT NULL
UNION ALL
SELECT [Group_Mobile_Phone] AS V, [Group_Mobile_Phone] AS D
FROM [DatabaseName]
WHERE MAINLKUPID = '{Import.TenantID}' AND [Group_Mobile_Phone] IS NOT NULL
UNION ALL
SELECT [Contact_Home_Phone] AS V, [Contact_Home_Phone] AS D
FROM [DatabaseName]
WHERE [Group_ID] = '{Script.V1}' AND [Contact_Home_Phone] IS NOT NULL
UNION ALL
SELECT [Contact_Mobile_Phone] AS V, [Contact_Mobile_Phone] AS D
FROM [DatabaseName]
WHERE [Group_ID] = '{Script.V1}' AND [Contact_Mobile_Phone] IS NOT NULL
UNION ALL
SELECT 'Enter other number' AS V, 'Enter other number' AS D
As you can see, it's not the most elegant thing I've coded. What I want it to look like, is something similar to this:
SELECT DISTINCT ([Contact_Mobile_Phone], [Contact_Home_Phone], [Contact_Work_Phone], [Group_Home_Phone], [Group_Mobile_Phone]) V,
([Contact_Mobile_Phone], [Contact_Home_Phone], [Contact_Work_Phone], [Group_Home_Phone], [Group_Mobile_Phone]) D
FROM [DatabaseName]
WHERE MAINLKUPID = '{Import.TenantID}' AND ([Contact_Mobile_Phone], [Contact_Home_Phone], [Contact_Work_Phone], [Group_Home_Phone], [Group_Mobile_Phone]) IS NOT NULL
AND MAINLKUPID = '{Import.TenantID}' AND ([Contact_Mobile_Phone], [Contact_Home_Phone], [Contact_Work_Phone], [Group_Home_Phone], [Group_Mobile_Phone]) IS NOT NULL
The criteria is that it needs to display the same results in both Column V and D. It needs to not pull through Null data, and it needs to only enter each number once into each column, even if they came from different columns originally. I'm aware the syntax for my ideal code isn't right, hoping someone can point me in the right direction. Thanks in advance.
Why don't you put the code in a stored procedure and just call "EXEC my_stored_procedure;"? That would be a lot shorter!
Can you explain why you are selecting the same column twice, but giving it different names? Perhaps you intend for the first to be a description. If so, it should be in single quotes not square braces -- but this will affect the duplicate elimination.
You can do the following to remove duplicates:
Replace the UNION ALL with UNION
You can do the following to reduce the size:
Remove all square braces
Remove as V and as D from all but the first query
If that is not short enough, then remove the NULL comparison and structure the query as:
select *
from (<your query here with no null comparisons and `union` instead of `union all`) x
where d is not null;
select phone_num as V,phone_num as D
from [DatabaseName] unpivot (phone_num for phone_type in ([Contact_Mobile_Phone],[Contact_Home_Phone],[Contact_Work_Phone],[Group_Home_Phone],[Group_Mobile_Phone])) u
where MAINLKUPID = '{Import.TenantID}'
union
select phone_num as V,phone_num as D
from [DatabaseName] unpivot (phone_num for phone_type in ([Contact_Home_Phone],[Contact_Mobile_Phone])) u
WHERE [Group_ID] = '{Script.V1}'
union all
SELECT 'Enter other number','Enter other number'

Detect all columns in an oracle table which have the same value in each row

Every day, the requests get weirder and weirder.
I have been asked to put together a query to detect which columns in a table contain the same value for all rows. I said "That needs to be done by program, so that we can do it in one pass of the table instead of N passes."
I have been overruled.
So long story short. I have this very simple query which demonstrates the problem. It makes 4 passes over the test set. I am looking for ideas for SQL Magery which do not involve adding indexes on every column, or writing a program, or taking a full human lifetime to run.
And sigh It needs to be able to work on any table.
Thanks in advance for your suggestions.
WITH TEST_CASE AS
(
SELECT 'X' A, 5 B, 'FRI' C, NULL D FROM DUAL UNION ALL
SELECT 'X' A, 3 B, 'FRI' C, NULL D FROM DUAL UNION ALL
SELECT 'X' A, 7 B, 'TUE' C, NULL D FROM DUAL
),
KOUNTS AS
(
SELECT SQRT(COUNT(*)) S, 'Column A' COLUMNS_WITH_SINGLE_VALUES
FROM TEST_CASE P, TEST_CASE Q
WHERE P.A = Q.A OR (P.A IS NULL AND Q.A IS NULL)
UNION ALL
SELECT SQRT(COUNT(*)) S, 'Column B' COLUMNS_WITH_SINGLE_VALUES
FROM TEST_CASE P, TEST_CASE Q
WHERE P.B = Q.B OR (P.B IS NULL AND Q.B IS NULL)
UNION ALL
SELECT SQRT(COUNT(*)) S, 'Column C' COLUMNS_WITH_SINGLE_VALUES
FROM TEST_CASE P, TEST_CASE Q
WHERE P.C = Q.C OR (P.C IS NULL AND Q.C IS NULL)
UNION ALL
SELECT SQRT(COUNT(*)) S, 'Column D' COLUMNS_WITH_SINGLE_VALUES
FROM TEST_CASE P, TEST_CASE Q
WHERE P.D = Q.D OR (P.D IS NULL AND Q.D IS NULL)
)
SELECT COLUMNS_WITH_SINGLE_VALUES
FROM KOUNTS
WHERE S = (SELECT COUNT(*) FROM TEST_CASE)
do you mean something like this?
WITH
TEST_CASE AS
(
SELECT 'X' A, 5 B, 'FRI' C, NULL D FROM DUAL UNION ALL
SELECT 'X' A, 3 B, 'FRI' C, NULL D FROM DUAL UNION ALL
SELECT 'X' A, 7 B, 'TUE' C, NULL D FROM DUAL
)
select case when min(A) = max(A) THEN 'A'
when min(B) = max(B) THEN 'B'
when min(C) = max(C) THEN 'C'
when min(D) = max(D) THEN 'D'
else 'No one'
end
from TEST_CASE
Edit
this works:
WITH
TEST_CASE AS
(
SELECT 'X' A, 5 B, 'FRI' C, NULL D FROM DUAL UNION ALL
SELECT 'X' A, 3 B, 'FRI' C, NULL D FROM DUAL UNION ALL
SELECT 'X' A, 7 B, 'TUE' C, NULL D FROM DUAL
)
select case when min(nvl(A,0)) = max(nvl(A,0)) THEN 'A ' end ||
case when min(nvl(B,0)) = max(nvl(B,0)) THEN 'B ' end ||
case when min(nvl(C,0)) = max(nvl(C,0)) THEN 'C ' end ||
case when min(nvl(D,0)) = max(nvl(D,0)) THEN 'D ' end c
from TEST_CASE
Bonus: I have also added the check for the null values, so the result now is: A and D
And the SQLFiddle demo for you.
Optimizer statistics can easily identify columns with more than one distinct value. After statistics are gathered a simple query against the data dictionary will return the results almost instantly.
The results will only be accurate on 10g if you use ESTIMATE_PERCENT = 100. The results will be accurate on 11g+ if you use ESTIMATE_PERCENT = 100 or AUTO_SAMPLE_SIZE.
Code
create table test_case(a varchar2(1), b number, c varchar2(3),d number,e number);
--I added a new test case, E. E has null and not-null values.
--This is a useful test because null and not-null values are counted separately.
insert into test_case
SELECT 'X' A, 5 B, 'FRI' C, NULL D, NULL E FROM DUAL UNION ALL
SELECT 'X' A, 3 B, 'FRI' C, NULL D, NULL E FROM DUAL UNION ALL
SELECT 'X' A, 7 B, 'TUE' C, NULL D, 1 E FROM DUAL;
--Gather stats with default settings, which uses AUTO_SAMPLE_SIZE.
--One advantage of this method is that you can quickly get information for many
--tables at one time.
begin
dbms_stats.gather_schema_stats(user);
end;
/
--All columns with more than one distinct value.
--Note that nulls and not-nulls are counted differently.
--Not-nulls are counted distinctly, nulls are counted total.
select owner, table_name, column_name
from dba_tab_columns
where owner = user
and num_distinct + least(num_nulls, 1) <= 1
order by column_name;
OWNER TABLE_NAME COLUMN_NAME
------- ---------- -----------
JHELLER TEST_CASE A
JHELLER TEST_CASE D
Performance
On 11g, this method might be about as fast as mucio's SQL statement. Options like cascade => false would improve performance by not analyzing indexes.
But the great thing about this method is that it also produces useful statistics. If the system is already gathering statistics at regular intervals the hard work may already be done.
Details about AUTO_SAMPLE_SIZE algorithm
AUTO_SAMPLE_SIZE was completely changed in 11g. It does not use sampling for estimating number of distinct values (NDV). Instead it scans the whole table and uses a hash-based distinct algorithm. This algorithm does not require large amounts of memory or temporary tablespace. It's much faster to read the whole table than to sort even a small part of it. The Oracle Optimizer blog has a good description of the algorithm here. For even more details, see this presentation by Amit Podder. (You'll want to scan through that PDF if you want to verify the details in my next section.)
Possibility of a wrong result
Although the new algorithm does not use a simple sampling algorithm it still does not count the number of distinct values 100% correctly. It's easy to find cases where the estimated number of distinct values is not the same as the actual. But if the number of distinct values are clearly inaccurate, how can they be trusted in this solution?
The potential inaccuracy comes from two sources - hash collisions and synopsis splitting. Synopsis splitting is the main source of inaccuracy but does not apply here. It only happens when there are 13864 distinct values. And it never throws out all of the values, the final estimate will certainly be much larger than 1.
The only real concern is what are the chances of there being 2 distinct values with a hash collision. With a 64-bit hash the chance could be as low as 1 in 18,446,744,073,709,551,616. Unfortunately I don't know the details of their hashing algorithm and don't know the real probability. I was unable to produce any collisions from some simple testing and from previous real-life tests. (One of my tests was to use large values, since some statistics operations only use the first N bytes of data.)
Now also consider that this will only happen if all of the distinct values in the table collide. What are the chances of there being a table with only two values that just happen to collide? Probably much less than the chance of winning the lottery and getting struck by a meteorite at the same time.
If you can live with the result on a single line, this should only scan once;
WITH TEST_CASE AS
(
SELECT 'X' A, 5 B, 'FRI' C, NULL D FROM DUAL UNION ALL
SELECT 'X' A, 3 B, 'FRI' C, NULL D FROM DUAL UNION ALL
SELECT 'X' A, 7 B, 'TUE' C, NULL D FROM DUAL
)
SELECT
CASE WHEN COUNT(DISTINCT A) +
COUNT(DISTINCT CASE WHEN A IS NULL THEN 1 END) = 1
THEN 1 ELSE 0 END SAME_A,
CASE WHEN COUNT(DISTINCT B) +
COUNT(DISTINCT CASE WHEN B IS NULL THEN 1 END) = 1
THEN 1 ELSE 0 END SAME_B,
CASE WHEN COUNT(DISTINCT C) +
COUNT(DISTINCT CASE WHEN C IS NULL THEN 1 END) = 1
THEN 1 ELSE 0 END SAME_C,
CASE WHEN COUNT(DISTINCT D) +
COUNT(DISTINCT CASE WHEN D IS NULL THEN 1 END) = 1
THEN 1 ELSE 0 END SAME_D
FROM TEST_CASE
An SQLfiddle to test with.
this will be done in a single scan
WITH
TEST_CASE AS
(
SELECT 'X' A, 5 B, 'FRI' C, NULL D FROM DUAL UNION ALL
SELECT 'X' A, 3 B, 'FRI' C, NULL D FROM DUAL UNION ALL
SELECT 'X' A, 7 B, 'TUE' C, NULL D FROM DUAL
)
select decode(count(distinct nvl(A,0)),1,'SINGLE','MULTP') COL_A,
decode(count(distinct nvl(B,0)),1,'SINGLE','MULTP') COL_B,
decode(count(distinct nvl(C,0)),1,'SINGLE','MULTP') COL_C,
decode(count(distinct nvl(D,0)),1,'SINGLE','MULTP') COL_D
from TEST_CASE

Can I add multiple columns to Totals

Using MS SQL 2012
I want to do something like
select a, b, c, a+b+c d
However a, b, c are complex computed columns, lets take a simple example
select case when x > 4 then 4 else x end a,
( select count(*) somethingElse) b,
a + b c
order by c
I hope that makes sense
You can use a nested query or a common table expression (CTE) for that. The CTE syntax is slightly cleaner - here it is:
WITH CTE (a, b)
AS
(
select
case when x > 4 then 4 else x end a,
count(*) somethingElse b
from my_table
)
SELECT
a, b, (a+b) as c
FROM CTE
ORDER BY c
I would probably do this:
SELECT
sub.a,
sub.b,
(sub.a + sub.b) as c,
FROM
(
select
case when x > 4 then 4 else x end a,
(select count(*) somethingElse) b
FROM MyTable
) sub
ORDER BY c
The easiest way is to do this:
select a,b,c,a+b+c d
from (select <whatever your calcs are for a,b,c>) x
order by c
That just creates a derived table consisting of your calculations for a, b, and c, and allows you to easily reference and sum them up!