Match a concatenated field to a list of variables - sql

Good Afternoon,
I'm trying to match a list of address fields (concatenated to give value ALL_ADDRESS) to a separate table that contains Suffixes, potentially hundreds of rows long.
my desired output is to show those entries where suffixes are part of the ALL_address variable (i.e PARIS STREET)
this works fine when I concatenate without a join, but when I begin to join I get an error:
select s.suffix,
x.key,
x.B_ADDR1_TX,
x.B_ADDR2_TX,
x.B_ADDR3_TX,
x.b_addr_city,
x.b_addr_postcd,
x.b_addr_cntry,
x.b_addr_state_cd,
x.B_ADDR1_TX || ' ' || x.B_ADDR2_TX || ' ' || x.B_ADDR3_TX || ' ' || x.b_addr_city || ' ' || x.b_addr_postcd || ' ' || x.b_addr_cntry || ' ' || x.b_addr_state_cd as All_Address
from test_table AS x
JOIN suffix_list AS s
WHERE
x.All_Address LIKE CONCAT('%',s.suffix,'%') ;
any help is greatly appreciated

I'm not sure what you are trying to do. But proper syntax requires an on clause for a join:
from test_table x join
suffix_list s
on x.All_Address LIKE CONCAT('%', s.suffix, '%')
As I recall, Oracle doesn't support as for table aliases, so your query might have other syntax problems as well.
In Oracle, this would more typically be written as:
from test_table x join
suffix_list s
on x.All_Address LIKE '%' || s.suffix || '%'

Haven't been using Oracle for a while but:
CREATE table t1 (
a varchar(5),
b varchar(5),
c varchar(5));
INSERT INTO t1 VALUES ('one','two','three');
INSERT INTO t1 VALUES ('two','nine','five');
INSERT INTO t1 VALUES ('two','one','one');
CREATE TABLE t2 (filter varchar(5));
INSERT INTO t2 VALUES ('one');
INSERT INTO t2 VALUES ('six');
WITH t1new AS (SELECT t1.*, a || ' ' || b || ' ' || c as address FROM t1)
SELECT t1new.*
FROM t1new,t2
WHERE address like CONCAT(CONCAT('%', t2.filter),'%')
Above example runs in liveql Oracle.

Related

SQL insert query with values like JUM'A ABDUL FATTAH causing missing , error

I am trying to insert data into a table which is derived dynamically.
I am selecting data from MASTER_TABLE which has col1 and col2. Depending on the values in col1 and col2, I am inserting the data in table SLAVE_col1value_col2value.
my sql query looks like:
insert_query_str :='INSERT INTO SLAVE_'
|| Col1
|| '_'
|| Col2
|| ' VALUES ( '''
|| CUST_NAME
|| ''','''
|| APPLICATION_DATA
|| ''')';
EXECUTE IMMEDIATE insert_query_str;
However, name like JUM'A ABDUL FATTAH cause the insert query string to look like this
INSERT INTO SLAVE_XYZ_ABC VALUES ( 'JUM'A ABDUL FATTAH','APPSPECIFICDATA');
and I get a missing comma error on the line.
I know if I double quote the name then this error can be sorted out but when I try that it hardcodes the || too and I don't get the real name in the query.
I am using Oracle SQL 11g and Toad.
Thank you in advance
Use placeholders together with USING clause:
insert_query_str :='INSERT INTO SLAVE_'
|| Col1
|| '_'
|| Col2
|| ' VALUES ( :cust_name, :app_data )';
EXECUTE IMMEDIATE insert_query_str USING CUST_NAME, APPLICATION_DATA;

Oracle Select which produce Insert into

I have query like
SELECT 'Insert INTO FOO(ID,NAME) values ('||id|| ',' ||NAME||');' as query
FROM FOO
This is just an example as I can't provide real query example. So, DON'T pay attention how it works just believe that it works as it should.
Question:
If NULL values exists in selected rows it just produces '' in INSERT INTO query. So I am getting an error while trying to execute insert query. How can I force SELECT query to return NULL instead of ''?
Example:
I have
Insert INTO FOO(ID,NAME) values (12,);
I want to have
Insert INTO FOO(ID,NAME) values (12,NULL);
Try using CASE EXPRESSION :
SELECT 'Insert INTO FOO(ID,NAME)
values ('||CASE WHEN id = '''' then 'NULL' else id end||','|| CASE WHEN NAME = '''' THEN null else NAME end || ');' as query
FROM FOO
I am guessing that NAME is a string, so it should be surrounded by single quotes. So, this might be the query you want:
SELECT 'Insert INTO FOO(ID,NAME) values (' || id || ', ''' || NAME ||''');' as query
FROM FOO;
If the issue is id, then you can do:
SELECT 'Insert INTO FOO(ID,NAME) values (' || (CASE WHEN id IS NULL THEN 'NULL' ELSE CAST(id AS VARCHAR2(255)) END) ||
', ''' || NAME ||''');' as query
FROM FOO;

Comparing column by column between two rows in Oracle DB

I need to write a query to compare column by column (ie: find differences) between two rows in the database. For example:
row1: 10 40 sometext 24
row2: 10 25 sometext 24
After the query executed, it should shows only the fields that have difference (ie: the second field)
Here's what I have done so far:
select table1.column1, table1.column2, table1.column3, table1.column4
from table1
where somefield in (field1, field2);
The above query will show me two rows one above another like this:
10 40 sometext 24
10 25 sometext 24
Then I have to manually do the comparison and it takes a lot of time b/c the row contains a lot of column.
So again my question is: How can I write a query that will show me only the columns that have differences??
Thanks
Use UNPIVOT clause (see http://www.oracle-developer.net/display.php?id=506) to turn columns into rows, then filter out the same rows (using GROUP BY HAVING COUNT and finally use PIVOT to get rows with different columns only.
To do this easily you need to query the metadata for the table to get each row. You can use the following code as a script.
Replace the define table_name with your table name and define yes_drop_it = NO. Put your raw WHERE syntax into the where_clause. The comparison logic always compares the first two rows returned for the where clause.
whenever sqlerror exit failure rollback;
set linesize 150
define test_tab_name = tst_cf_cols
define yes_drop_it = YES
define order_by = 1, 2
define where_clause = 1 = 1
define tab_owner = user
<<clearfirst>> begin
for clearout in (
select 'drop table ' || table_name as cmd
from all_tables
where owner = &&tab_owner and table_name = upper('&&test_tab_name')
and '&&yes_drop_it' = 'YES'
) loop
execute immediate clearout.cmd;
execute immediate '
create table &&test_tab_name as
select 10 as column1, 40 as column2, ''sometext'' as column3, 24 as column4 from dual
union all
select 10 as column1, 25 as column2, ''sometext'' as column3, 24 as column4 from dual
';
end loop;
end;
/
column cfsynt format a4000 word_wrap new_value comparison_syntax
with parms as (select 'parmquery' as cte_name, 'row_a' as corr_name_1, 'row_b' as corr_name_2 from dual)
select
'select * from (select ' || LISTAGG(cfcol || ' AS cf_' || trim (to_char (column_id, '000')) || '_' || column_name
, chr(13) || ', ') WITHIN GROUP (order by column_id)
|| chr(13) || ' from (select * from parmquery where row_number = 1) ' || corr_name_1
|| chr(13) || ', (select * from parmquery where row_number = 2) ' || corr_name_2
|| chr(13) || ') where ''DIFFERENT'' IN (' || LISTAGG ('cf_' || trim (to_char (column_id, '000')) || '_' || column_name, chr(13) || ', ') within group (order by column_id) || ')'
as cfsynt
from parms, (
select
'decode (' || corr_name_1 || '.' || column_name || ', ' || corr_name_2
|| '.' || column_name || ', ''SAME'', ''DIFFERENT'')'
as cfcol,
column_name,
column_id
from
parms,
all_tab_columns
where
owner = &&tab_owner and table_name = upper ('&&test_tab_name')
);
with parmquery as (select rownum as row_number, vals.* from (
select * from &&test_tab_name
where &&where_clause
order by &&order_by
) vals
) &&comparison_syntax
;

How to convert two rows into key-value json object in postgresql?

I have a data model like the following which is simplified to show you only this problem (SQL Fiddle Link at the bottom):
A person is represented in the database as a meta table row with a name and with multiple attributes which are stored in the data table as key-value pair (key and value are in separate columns).
Expected Result
Now I would like to retrieve all users with all their attributes. The attributes should be returned as json object in a separate column. For example:
name, data
Florian, { "age":23, "color":"blue" }
Markus, { "age":24, "color":"green" }
My Approach
Now my problem is, that I couldn't find a way to create a key-value pair in postgres. I tried following:
SELECT
name,
array_to_json(array_agg(row(d.key, d.value))) AS data
FROM meta AS m
JOIN (
SELECT d.fk_id, d.key, d.value AS value FROM data AS d
) AS d
ON d.fk_id = m.id
GROUP BY m.name;
But it returns this as data column:
[{"f1":"age","f2":24},{"f1":"color","f2":"blue"}]
Other Solutions
I know there is the function crosstab which enables me to turn the data table into a key as column and value as row table. But this is not dynamic. And I don't know how many attributes a person has in the data table. So this is not an option.
I could also create a json like string with the two row values and aggregate them. But maybe there is a nicer solution.
And no, it is not possible to change the data-model because the real data model is already in use of multiple parties.
SQLFiddle
Check and test out the fiddle i've created for this question:
http://sqlfiddle.com/#!15/bd579/14
Use the aggregate function json_object_agg(key, value):
select
name,
json_object_agg(key, value) as data
from data
join meta on fk_id = id
group by 1;
Db<>Fiddle.
The function was introduced in Postgres 9.4.
I found a way to return crosstab data with dynamic columns. Maybe rewriting this will be better to suit your needs:
CREATE OR REPLACE FUNCTION report.usp_pivot_query_amount_generate(
i_group_id INT[],
i_start_date TIMESTAMPTZ,
i_end_date TIMESTAMPTZ,
i_interval INT
) RETURNS TABLE (
tab TEXT
) AS $ab$
DECLARE
_key_id TEXT;
_text_op TEXT = '';
_ret TEXT;
BEGIN
-- SELECT DISTNICT for query results
FOR _key_id IN
SELECT DISTINCT at_name
FROM report.company_data_date cd
JOIN report.company_data_amount cda ON cd.id = cda.company_data_date_id
JOIN report.amount_types at ON cda.amount_type_id = at.id
WHERE date_start BETWEEN i_start_date AND i_end_date
AND group_id = ANY (i_group_id)
AND interval_type_id = i_interval
LOOP
-- build function_call with datatype of column
IF char_length(_text_op) > 1 THEN
_text_op := _text_op || ', ' || _key_id || ' NUMERIC(20,2)';
ELSE
_text_op := _text_op || _key_id || ' NUMERIC(20,2)';
END IF;
END LOOP;
-- build query with parameter filters
_ret = '
SELECT * FROM crosstab(''SELECT date_start, at.at_name, cda.amount ct
FROM report.company_data_date cd
JOIN report.company_data_amount cda ON cd.id = cda.company_data_date_id
JOIN report.amount_types at ON cda.amount_type_id = at.id
WHERE date_start between $$' || i_start_date::TEXT || '$$ AND $$' || i_end_date::TEXT || '$$
AND interval_type_id = ' || i_interval::TEXT || '
AND group_id = ANY (ARRAY[' || array_to_string(i_group_id, ',') || '])
ORDER BY date_start'')
AS ct (date_start timestamptz, ' || _text_op || ')';
RETURN QUERY
SELECT _ret;
END;
$ab$ LANGUAGE 'plpgsql';
Call the function to get the string, then execute the string. I think I tried executing it in the function, but it didn't work well.
I ran into the same problem when I needed to update some JSON and remove a few elements in my database. This query below worked well enough for me, as it preserves the string quotes but does not add them to numbers.
select
'{' || substr(x.arr, 3, length(x.arr) - 4) || '}'
from
(
select
replace(replace(cast(array_agg(xx) as varchar), '\"', '"'), '","', ', ') as arr
from
(
select
elem.key,
elem.value,
'"' || elem.key || '":' || elem.value as xx
from
quote q
cross join
json_each(q.detail::json -> 'bQuoteDetail'-> 'quoteHC'->0) as elem
where
elem.key != 'allRiskItems'
) f
) x

oracle insert using DBlink

I have two different databases, say DB1 and DB2. In both DBs I have a common table called test_emp, but there is a difference in the columns of the tables:
DB1
-----
desc table1
--------------
empno number
ename varchar2
sal number
hiredate date
deptno number
DB2
-----
desc table2
--------------
empno number
ename varchar2
sal number
insert into table1 select * from table2#DBLink_Name
But here I have a problem with number of columns, target table has more columns than the source.
Here I can't specify column_names because I am passing table as dynamic.
Can you somebody please help?
You could look at the oracle data dictionary/metadata tables on both databases and generate the necessary sql for the insert. if i_table_name is your input.
select list_agg(column_name,',') columns -- comma separated list of columns
from (
select column_name
from all_tab_cols
where owner = 'XYZ'
and table_name = i_table_name
intersect
select column_name
from all_tab_cols#remote_database
where owner = 'XYZ' -- could be different (?)
and table_name = i_table_name
)
You could then use this string (comma separated list of colums) to do something along the lines of..
v_sql := 'insert into ' || i_table_name || ' ' || '(' ||
v_column_list || ')' ||
' select ' || '( ' || v_column_list ||
' from ' || i_table_name || '#' || remote_db_name;
dbms_output.put_line(v_sql);
execute immediate v_sql;
I haven't tested the code. Make sure you print the insert and confirm it matches your expectations.
use
INSERT INTO table1
SELECT a.*,
NULL,
NULL
FROM table2#dblink_