SQL oracle update using several tables - sql

Firstly, I'm not used to SQL syntax at all.
I face the following issue. T1 and C2 are my two tables. I'd look to do this:
UPDATE T1 t
SET t.adr = C2.adr
WHERE t.cli != C2.cli;
I have the following errors : C2.adr and C2.cli : invalid identifier
Am I supposed to do this :
UPDATE T1 t, C2 c
SET t.adr = c.adr
WHERE t.cli != c.cli;
It's just a bit weird, because I don't update C2, but maybe, it's just the syntax, where the tables are referenced after the action (here update).

This will do
UPDATE T1 t, C2 c
SET t.adr = c.adr
WHERE t.cli != c.cli;

It would be helpful if you expanded on what you were trying to achieve as the query you've posted is rather abstract and there looks like there ought to be another column which can be used to connect rows in T1 and C1.
However, from what you have asked, you can try something like this:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE T1 ( cli, adr ) AS
SELECT 1, 1 FROM DUAL
UNION ALL SELECT 2, 2 FROM DUAL
UNION ALL SELECT 3, 3 FROM DUAL
UNION ALL SELECT 4, 4 FROM DUAL;
CREATE TABLE C1 ( cli, adr ) AS
SELECT 1, 5 FROM DUAL
UNION ALL SELECT 2, 6 FROM DUAL
UNION ALL SELECT 3, 7 FROM DUAL
UNION ALL SELECT 4, 8 FROM DUAL;
Query 1:
UPDATE T1 t
SET adr = ( SELECT MIN( c.adr )
FROM C1 c
WHERE c.cli <> t.cli )
Query 2:
SELECT * FROM T1
Results:
| CLI | ADR |
|-----|-----|
| 1 | 6 |
| 2 | 5 |
| 3 | 5 |
| 4 | 5 |
Its not quite like you asked for as I've had to put MIN( c.adr ) in to ensure the sub-query returns a single row but it gives you an example of how to reference a second table in an update query.
If you don't aggregate using MIN() then (for the test data I've created) the not equals condition will match multiple rows and you get an error:
Query 3:
UPDATE T1 t
SET adr = ( SELECT c.adr
FROM C1 c
WHERE c.cli <> t.cli )
Results:
ORA-01427: single-row subquery returns more than one row : UPDATE T1 t SET adr = ( SELECT c.adr FROM C1 c WHERE c.cli <> t.cli )

Related

join multiple times in SQL

These are the 2 tables.
Tech_data:
Id Tech Agent1_id Agent2_ID
1 JAVA 1 2
2 SQL 3 4
Agent_table
Id Name
1 Mike
2 John
3 Jim
4 Baron
I need to write a query to bring the below output
TECH_ID Tech Agent1_Name Agent2_Name
1 Java Mike John
2 SQL Jim Baron
I wrote LEFT OUTER JOIN ON tech_id=agent1_id, but i do not know how to join 2 ids in ON condition.
To prevent having to do multiple joins to the same table, you can unpivot, join and then pivot (then if you had 50 ID columns you would still only need to perform one join):
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE Tech_data (Id, Tech, Agent1_id, Agent2_ID ) AS
SELECT 1, 'JAVA', 1, 2 FROM DUAL UNION ALL
SELECT 2, 'SQL', 3, 4 FROM DUAL;
CREATE TABLE Agent_table ( Id, Name ) AS
SELECT 1, 'Mike' FROM DUAL UNION ALL
SELECT 2, 'John' FROM DUAL UNION ALL
SELECT 3, 'Jim' FROM DUAL UNION ALL
SELECT 4, 'Baron' FROM DUAL;
Query 1:
SELECT *
FROM (
SELECT t.id,
t.tech,
t.num,
a.name
FROM (
SELECT *
FROM tech_data
UNPIVOT ( Agent_ID FOR num IN ( Agent1_id AS 1, Agent2_id AS 2 ) )
) t
INNER JOIN Agent_table a
ON ( t.agent_id = a.id )
)
PIVOT ( MAX( name ) FOR num IN ( 1 AS Agent1_Name, 2 AS Agent2_Name ) )
Results:
| ID | TECH | AGENT1_NAME | AGENT2_NAME |
|----|------|-------------|-------------|
| 1 | JAVA | Mike | John |
| 2 | SQL | Jim | Baron |
You can add a second left outer join just like the one you have used, on the same table by giving them different aliases as follows.
select t.Id tech_id, t.tech, a1.name, a2.name
from tech_data t
left outer join agent_table a1 on a1.Id = t.agent1_id
left outer join agent_table a2 on a2.Id = t.agent2_Id;
Check the fiddle below:
http://sqlfiddle.com/#!4/73f02b/1

is not distinct from in join on clause oracle

I have a query structured with the left outer join like so:
left outer JOIN GA_LOAN GA
ON LOAN.LOAN_TYPE = GA.LOAN_TYP
AND LOAN.DT = GA.GUARANTY_DT
AND LOAN.FFEL_DUP_ID = GA.SEP_LOAN_IND
AND LOAN.SCH_BR_CODE = GA.ORIG_SCHL_CD
AND STU.CURR_SSN = GA.STU_SSN
AND STU.DOB = GA.DOB
and stu.curr_fst = ga.stu_first_nam
--and (plus_bor.curr_ssn is not distinct from ga.plus_brwr_ssn )
When I add the commented out line, I get the following error.
ORA-00908: missing NULL keyword
00908. 00000 - "missing NULL keyword"
*Cause:
*Action:
is not distinct from works fine in this structure in DB2, but Oracle is giving me issues. Any suggestions?
I get no errors if I replaced is not distinct from with a = but that isn't the same logically.
is not distinct from with give a match if both values are null, where as = would not match in this case.
The simplest way to emulate IS [ NOT ] DISTINCT FROM in Oracle is by using DECODE:
-- a IS DISTINCT FROM b
DECODE(a, b, 1, 0) = 0
-- a IS NOT DISTINCT FROM b
DECODE(a, b, 1, 0) = 1
This is what you're getting when you're using jOOQ's SQL dialect translator. A dbfiddle for this:
WITH t (x) AS (
SELECT 1 FROM dual UNION ALL
SELECT 2 FROM dual UNION ALL
SELECT null FROM dual
)
SELECT
t1.x AS x1,
t2.x AS x2,
DECODE(t1.x, t2.x, 1, 0) AS not_distinct
FROM t t1, t t2
ORDER BY 1, 2
Yields:
X1 | X2 | NOT_DISTINCT
-----+------+-------------
1 | 1 | 1
1 | 2 | 0
1 | null | 0
2 | 1 | 0
2 | 2 | 1
2 | null | 0
null | 1 | 0
null | 2 | 0
null | null | 1
You could emulate IS DISTINCT FROM by using NOT EXISTS combined with INTERSECT:
plus_bor.curr_ssn IS DISTINCT FROM ga.plus_brwr_ssn
<=>
NOT EXISTS (SELECT plus_bor.curr_ssn FROM dual INTERSECT
SELECT ga.plus_brwr_ssn FROM dual);
Example:
WITH cte(a,b) AS (
SELECT 1, NULL FROM dual UNION ALL
SELECT 1,2 FROM dual UNION ALL
SELECT 1,1 FROM dual UNION ALL
SELECT NULL, 1 FROM dual UNION ALL
SELECT NULL, NULL FROM dual
)
SELECT *
FROM cte
WHERE NOT EXISTS (SELECT a FROM dual INTERSECT
SELECT b FROM dual)
Rextester Demo
Output:
A B
------------
1 NULL
1 2
NULL 1
And in your case IS NOT DISTINCT FROM is simply EXISTS:
plus_bor.curr_ssn IS NOT DISTINCT FROM ga.plus_brwr_ssn
<=>
EXISTS (SELECT plus_bor.curr_ssn FROM dual INTERSECT
SELECT ga.plus_brwr_ssn FROM dual);
Example:
WITH cte(a,b) AS (
SELECT 1, NULL FROM dual UNION ALL
SELECT 1,2 FROM dual UNION ALL
SELECT 1,1 FROM dual UNION ALL
SELECT NULL, 1 FROM dual UNION ALL
SELECT NULL, NULL FROM dual
)
SELECT *
FROM cte
WHERE EXISTS (SELECT a FROM dual INTERSECT
SELECT b FROM dual);
Output:
A B
1 1
NULL NULL
Rextester Demo2
ADDENDUM
This approach has one big advantage over COALESCE/NVL approach as proposed in comments.
You don't have to think about default neutral value dependent on datatype.
For example if column is datatype DATE/INT/TEXT then you have to write something like:
coalesce(col1,DATE '1900-01-01') = coalesce(col2,DATE '1900-01-01')
coalesce(col1, 0) = coalesce(col2, 0)
coalesce(col1, ' ') = coalesce(col2, ' ')
There is of course slight chance of collision. For example:
coalesce(col1, 0) = coalesce(col2, 0)
=>
col1 = NULL
col2 = 0
and we have incorrect match!!!

How to duplicate each row in sql query?

Suppose we have a query
SELECT * FROM my_table ORDER BY id
which results in
id | title
-----------
1 | 'ABC'
2 | 'DEF'
3 | 'GHI'
How could I modify given select statement to have each row duplicated in the result set like this:
id | title
-----------
1 | 'ABC'
1 | 'ABC'
2 | 'DEF'
2 | 'DEF'
3 | 'GHI'
3 | 'GHI'
Try this...
SELECT * FROM my_table
UNION ALL
SELECT * FROM my_table
ORDER BY id
You can use union all, but I like using cross join:
select *
from MyTable cross join
(select 1 from dual union all select 2 from dual) n
order by id;
The reason I like the cross join is in the case where MyTable is really some complicated subquery. Although the query optimizer might evaluate it only once, you can't really depend on that fact. So the performance should be better in this case.
You could cross join to a row generator, the numeric value indicates how many duplicates per original you want.
select *
from my_table
cross join
(select null
from dual
connect by level <= 2)
order by id

SQL - Select what is not in second table from assocciative

I have a table "person", an associative table "person_vaccination" and a table "vaccination".
I want to get the person who has missing vaccinations but so far I only got it to work when I have the id.
SELECT vac.VACCINATION_Name
FROM VACCINATION vac
WHERE vac.VACCINATION_NUMBER NOT IN
(SELECT v.VACCINATION_NUMBER
FROM PERSON per
Join PERSON_VACCINATION pv ON per.PERSON_NUMBER = pv.PERSON_NUMBER
JOIN VACCINATION v ON pv.VACCINATION_NUMBER = v.VACCINATION_NUMBER
WHERE per.PERSON_NUMBER = 6)
It works fine but how do I get all the people missing their vaccinations? (ex:
555 , Vacccination 1
555 , Vacccination 2
666 , Vacccination 1)
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE VACCINATION ( VACCINATION_NUMBER, VACCINATION_NAME ) AS
SELECT 1, 'Vac 1' FROM DUAL
UNION ALL SELECT 2, 'Vac 2' FROM DUAL
UNION ALL SELECT 3, 'Vac 3' FROM DUAL
UNION ALL SELECT 4, 'Vac 4' FROM DUAL;
CREATE TABLE PERSON_VACCINATION ( VACCINATION_NUMBER, PERSON_NUMBER ) AS
SELECT 1, 1 FROM DUAL
UNION ALL SELECT 2, 1 FROM DUAL
UNION ALL SELECT 3, 1 FROM DUAL
UNION ALL SELECT 4, 1 FROM DUAL
UNION ALL SELECT 1, 2 FROM DUAL
UNION ALL SELECT 2, 2 FROM DUAL
UNION ALL SELECT 3, 2 FROM DUAL;
CREATE TABLE PERSON ( PERSON_NUMBER, PERSON_NAME ) AS
SELECT 1, 'P1' FROM DUAL
UNION ALL SELECT 2, 'P2' FROM DUAL
UNION ALL SELECT 3, 'P3' FROM DUAL;
Query 1:
SELECT p.PERSON_NAME,
v.VACCINATION_NAME
FROM VACCINATION v
CROSS JOIN
PERSON p
WHERE NOT EXISTS ( SELECT 1
FROM PERSON_VACCINATION pv
WHERE pv.VACCINATION_NUMBER = v.VACCINATION_NUMBER
AND pv.PERSON_NUMBER = p.PERSON_NUMBER )
ORDER BY p.PERSON_NAME,
p.PERSON_NUMBER,
v.VACCINATION_NAME,
v.VACCINATION_NUMBER
Results:
| PERSON_NAME | VACCINATION_NAME |
|-------------|------------------|
| P2 | Vac 4 |
| P3 | Vac 1 |
| P3 | Vac 2 |
| P3 | Vac 3 |
| P3 | Vac 4 |
Instead of an INNER JOIN, you should use LEFT JOIN.
Take a look at this link: http://www.w3schools.com/sql/sql_join_left.asp
If you are after people with no vaccinations at all, then you can use a LEFT OUTER JOIN between PERSON and PERSON_VACCINATION, then find all entries where a PERSON_VACCINATION column is NULL.
SELECT PERSON_NUMBER
FROM PERSON P
LEFT OUTER JOIN
PERSON_VACCINATION PV
ON P.PERSON_NUMBER = PV.PERSON_NUMBER
WHERE PV.PERSON_NUMBER IS NULL
If you are unfamiliar with LEFT OUTER JOIN, it tries to find matching rows in PERSON_VACCINATION for each row in PERSON. If there are no matching rows, it leaves the PERSON row in the result set, and shows NULL values for all columns in the PERSON_VACCINATION table.
If you are looking for a list of people and the vaccinations they do not have then #MT0's answer is correct. You need to create a result set containing all possible combinations of PERSON and VACCINATION (a Cross Join), then check which of those combinations actually exist in PERSON_VACCINATION. Any entry that does not exist is are your missing vaccinations.

Select data from table with update if particular row does not exist

I have a simple table TBL consisting of two columns Err_type and Val.
Need to select all date from it. Seems to be simple, but it gets uglier when particular row does not exist.
with cte as (
select TBL.Err_type, TBL.Val from TBL
where TBL.Err_type = 4 or TBL.Err_type = 2
)
select * from cte
There are possibilities that "4" or "2" might not exist in given datetime range. So i need to insert the missing row ("4" or "2" for Err_type and "0" for Val) and then get the table.
e.g.
Err_type | Val
---------------
4 | 50
2 | 0
instead of
Err_type | Val
---------------
4 | 50
Use following sql that will check for existence of record and if not found then insert.
WITH cte
AS (SELECT *
FROM TBL
WHERE TBL.Err_Type IN ( 4, 2 )
UNION
SELECT 4,
0
WHERE NOT EXISTS (SELECT 1
FROM TBL
WHERE TBL.Err_Type = 4)
UNION
SELECT 2,
0
WHERE NOT EXISTS (SELECT 1
FROM TBL
WHERE TBL.Err_Type = 2))
SELECT *
FROM cte