Vertica - Join on MAX(Date) - vertica

I m trying to replicate this sql syntax in Vertica, but it returns "ERROR: Subqueries in the ON clause are not supported".
The aim is to join two tables, table1 and table2, on column and date, if a.date = b.date or the closest but lesser b.date.
Any hint?
SELECT *
FROM table1 a
LEFT JOIN table2 b
ON a.column = b.column
AND b.Date = (SELECT MAX (b2.Date)
FROM table2 b2
WHERE a.column = b2.column
AND b2.Date <= a.Date)

Vertica has something handier for that: the event series join. With that, you can OUTER JOIN two tables so that they match the same or the immediately preceding value of the join column of the other table. The predicate is INTERPOLATE PREVIOUS VALUE instead of an equi-predicate.
Here's an example with the oil pressure curve and the engine rpm curve with the timestamp matching only once.
DROP TABLE IF EXISTS oilpressure;
CREATE TABLE oilpressure (
op_vid,op_ts,op_psi
) AS (
SELECT 42,TIMESTAMP '2020-04-01 07:00:00', 25.356
UNION ALL SELECT 42,TIMESTAMP '2020-04-01 07:00:12', 35.124
UNION ALL SELECT 42,TIMESTAMP '2020-04-01 07:00:23', 47.056
UNION ALL SELECT 42,TIMESTAMP '2020-04-01 07:00:34', 45.225
)
;
DROP TABLE IF EXISTS revspeed;
CREATE TABLE revspeed (
rs_vid,rs_ts,rpm
) AS (
SELECT 42,TIMESTAMP '2020-04-01 07:00:00', 2201
UNION ALL SELECT 42,TIMESTAMP '2020-04-01 07:00:10', 3508
UNION ALL SELECT 42,TIMESTAMP '2020-04-01 07:00:20', 6504
UNION ALL SELECT 42,TIMESTAMP '2020-04-01 07:00:30', 6608
)
;
-- without
\pset null '(null)'
SELECT *
FROM oilpressure
LEFT OUTER JOIN revspeed
ON op_vid=rs_vid AND op_ts=rs_ts ;
-- out Null display is "(null)".
-- out op_vid | op_ts | op_psi | rs_vid | rs_ts | rpm
-- out --------+---------------------+--------+--------+---------------------+--------
-- out 42 | 2020-04-01 07:00:00 | 25.356 | 42 | 2020-04-01 07:00:00 | 2201
-- out 42 | 2020-04-01 07:00:12 | 35.124 | (null) | (null) | (null)
-- out 42 | 2020-04-01 07:00:23 | 47.056 | (null) | (null) | (null)
-- out 42 | 2020-04-01 07:00:34 | 45.225 | (null) | (null) | (null)
-- with
SELECT *
FROM oilpressure
LEFT OUTER JOIN revspeed
ON op_vid=rs_vid AND rs_ts INTERPOLATE PREVIOUS VALUE op_ts;
-- out op_vid | op_ts | op_psi | rs_vid | rs_ts | rpm
-- out --------+---------------------+--------+--------+---------------------+------
-- out 42 | 2020-04-01 07:00:00 | 25.356 | 42 | 2020-04-01 07:00:00 | 2201
-- out 42 | 2020-04-01 07:00:12 | 35.124 | 42 | 2020-04-01 07:00:10 | 3508
-- out 42 | 2020-04-01 07:00:23 | 47.056 | 42 | 2020-04-01 07:00:20 | 6504
-- out 42 | 2020-04-01 07:00:34 | 45.225 | 42 | 2020-04-01 07:00:30 | 6608

Related

Null Values in Left Outer Join Table

I have this situation in Oracle while trying to do a select from 2 tables:
Table1
-------------
Col1 Col2
-------------
1 abc
2 aa
3 lab
4 mm
5 nn
6 kk
7 pp
Table 2
-----------
Col1 Col2
----------
4 xxx
7 yyy
(null) zzz
I want all rows including (null) zzz of Table2 in my select.
What I am doing is -
SELECT column1, column2,….FROM Table1, Table2 Where table1.col1 = Table2.col1(+)
But it does not give me the row with NULL value in Table2.
I tried putting OR conditions with left outer join on col1 but it does not work.
SELECT column1, column2,…. FROM Table1, Table2 Where table1.col1 = Table2.col1(+) OR Table2.Col1 IS NULL
This is expected output:
Col1 Table1.Col2 Table2.Col2
--------------------------------
1 abc (null)
2 aa (null)
3 lab (null)
4 mm xxx
5 nn (null)
6 kk (null)
7 pp yyy
(null) (null) zzz
Its seems what you want is a FULL OUTER JOIN
SQL Fiddle
Query 1:
SELECT
t1.COl1
,t1.col2
,t2.col2
FROM Table1 t1
FULL OUTER JOIN Table2 t2 ON
( t1.col1 = t2.col1 )
ORDER BY Col1
Results:
| COL1 | COL2 | COL2 |
|--------|--------|--------|
| 1 | abc | (null) |
| 2 | aa | (null) |
| 3 | lab | (null) |
| 4 | mm | xxx |
| 5 | nn | (null) |
| 6 | kk | (null) |
| 7 | pp | yyy |
| (null) | (null) | zzz |
An important advice to you is to get rid of (+) outer join notation and use ANSI .. JOIN ON syntax.

How to remove duplicate values from SQL inner join tables?

I have two tables:
Table 1:
+-----------+-----------+------------------+
| ID | Value | other |
+-----------+-----------+------------------+
| 123456 | 5 | 12 |
| 987654 | 7 | 15 |
| 456789 | 6 | 22 |
+-----------+-----------+------------------+
Table 2:
+-----------+-----------+------------------+
| ID | Type | other |
+-----------+-----------+------------------+
| 123456 | 00 | 2 |
| 123456 | 01 | 6 |
| 123456 | 02 | 4 |
| 987654 | 00 | 7 |
| 987654 | 01 | 8 |
| 456789 | 00 | 6 |
| 456789 | 01 | 16 |
+-----------+-----------+------------------+
Now I perform the inner join:
SELECT
table1.ID, table2.TYPE, table1.value, table2.other
FROM
table1 INNER JOIN table2 ON table1.ID = table2.ID
Here the SQLfiddle
Result Table:
+-----------+-----------+---------+------------------+
| ID | Type | Value | other |
+-----------+-----------+---------+------------------+
| 123456 | 00 | 5 | 2 |
| 123456 | 01 | 5 | 6 |
| 123456 | 02 | 5 | 4 |
| 987654 | 00 | 7 | 7 |
| 987654 | 01 | 7 | 8 |
| 456789 | 00 | 6 | 6 |
| 456789 | 01 | 6 | 16 |
+-----------+-----------+---------+------------------+
This is totally what I expected but not what I need.
Because if I now want to get the Value per ID the Value gets doubled or tripled for the first cause.
Desired Table:
+-----------+-----------+---------+------------------+
| ID | Type | Value | other |
+-----------+-----------+---------+------------------+
| 123456 | 00 | 5 | 2 |
| 123456 | 01 | - | 6 |
| 123456 | 02 | - | 4 |
| 987654 | 00 | 7 | 7 |
| 987654 | 01 | - | 8 |
| 456789 | 00 | 6 | 6 |
| 456789 | 01 | - | 16 |
+-----------+-----------+---------+------------------+
I tried to achieve a similar output by counting the rows per id and dividing the sum of Value by that count but it did not seem to work and is not the desired output.
Also, I tried grouping but this did not seem to achieve the desired output.
One thing to mention is that the DB I am working with is an ORACLE SQL DB.
How about this:
select table1.id
, table2.type
, case
when row_number() over (partition by table1.id order by table2.type) = 1
then table1.value
end as "VALUE"
, table2.other
from table1
join table2 on table1.id = table2.id
order by 1, 2;
(This is Oracle SQL syntax. Your SQL Fiddle (thanks!) was set to MySQL, which as far as I know doesn't have analytic functions like row_number().)
A way to get the result.
select t1.ID,
t2.type,
t1.value,
t2.other
from table1 t1 inner join table2 t2
ON t1.ID = t2.ID
inner join (select ID, min(type) mv
from table2
group by id) m
on t2.id = m.id
and t2.type = m.mv
union all
select t1.ID,
t2type,
null,
t2.other
from table1 t1 inner join table2 t2
ON t1.ID = t2.ID
and not exists (
select 1 from (
select ID, min(type) mv
from table2
group by id) m
where t2.id = m.id
and t2.type = m.mv
)
order by id,type
You can use a CASE block to display NULL for which it is NOT equal to MIN value of type
SELECT table1.ID,
table2.TYPE,
CASE
WHEN table2.TYPE =
MIN (table2.TYPE)
OVER (PARTITION BY table1.id ORDER BY table2.TYPE)
THEN
Table1.VALUE
END
VALUE,
table2.other
FROM table1 INNER JOIN table2 ON table1.ID = table2.ID;

Edit Total line in Oracle report 1

The data structure:
sql-> desc t1
- List item
p_code number,
acc_date date ,
debit number,
credit number
Data inside my table:
sql-> select * from t1;
| p_code | acc_date | debit | credit |
| 001 | 01-01-15 | 100 | 25 |
| 001 | 02-01-15 | 0 | 125 |
| 001 | 03-01-15 | 415 | 85 |
I would like to do something like this:
select * from t1
where acc_date between :fromdate and :todate
union all
select p_code, (sum(nvl(debit,0))- sum(nvl(credit,0))) open_balance
from t1
where acc_date < :fromdate
;
But, I can't figure out what are my mistakes.
Type and number of columns in union must be the same -
select p_code, acc_date, debit, credit, null as open_balance
from t1
where acc_date between :fromdate and :todate
union all
select p_code, null as acc_date, null as debit, null as credit,
(sum(nvl(debit, 0)) - sum(nvl(credit, 0))) open_balance
from t1
where acc_date < :fromdate

why do they use (+) in where clause for checking not null in a column eg emp_name(+) IS NOT NULL

Why do we use a (+) operator in the where clause for instance emp_name(+) IS NOT NULL, emp_name IS NOT NULL AND emp_name(+) IS NOT NULL is the same
Because removing the (+) from the column you're checking is not null turns the join from an outer join into what is effectively an inner join. Leaving the (+) in tells oracle to get all rows from the "main" table, and then match any rows from the outer joined table where that column is not null.
See the below for an example of why the "extra" (+) is needed:
with t1 as (select 1 id, 'a' val from dual union all
select 2 id, 'b' val from dual union all
select 3 id, 'c' val from dual),
t2 as (select 1 id, null val from dual union all
select 3 id, 'd' val from dual)
select *
from t1,
t2
where t1.id = t2.id (+)
and t2.val (+) is not null
order by t1.id;
ID VAL ID_1 VAL_1
---------- --- ---------- -----
1 a
2 b
3 c 3 d
with t1 as (select 1 id, 'a' val from dual union all
select 2 id, 'b' val from dual union all
select 3 id, 'c' val from dual),
t2 as (select 1 id, null val from dual union all
select 3 id, 'd' val from dual)
select *
from t1,
t2
where t1.id = t2.id (+)
and t2.val is not null
order by t1.id;
ID VAL ID_1 VAL_1
---------- --- ---------- -----
3 c 3 d
You can see the difference easier if you convert the query to the ANSI join syntax:
with t1 as (select 1 id, 'a' val from dual union all
select 2 id, 'b' val from dual union all
select 3 id, 'c' val from dual),
t2 as (select 1 id, null val from dual union all
select 3 id, 'd' val from dual)
select *
from t1
left outer join t2 on (t1.id = t2.id and t2.val is not null)
order by t1.id;
ID VAL ID_1 VAL_1
---------- --- ---------- -----
1 a
2 b
3 c 3 d
with t1 as (select 1 id, 'a' val from dual union all
select 2 id, 'b' val from dual union all
select 3 id, 'c' val from dual),
t2 as (select 1 id, null val from dual union all
select 3 id, 'd' val from dual)
select *
from t1
left outer join t2 on (t1.id = t2.id)
where t2.val is not null
order by t1.id;
ID VAL ID_1 VAL_1
---------- --- ---------- -----
3 c 3 d
In other words, it's the difference between the "col is not null" predicate being a part of the outer join condition, or a filter in the where clause.
You'll note as well that having the "t2.val is not null" in the where clause has the effect of turning the outer join into an inner join, despite the fact that you've requested an outer join:
with t1 as (select 1 id, 'a' val from dual union all
select 2 id, 'b' val from dual union all
select 3 id, 'c' val from dual),
t2 as (select 1 id, null val from dual union all
select 3 id, 'd' val from dual)
select *
from t1
left outer join t2 on (t1.id = t2.id)
--where t2.val is not null
order by t1.id;
-----------------------------------------------------------------------------------------------------
| Id | Operation | Name | E-Rows |E-Bytes| Cost (%CPU)| E-Time | OMem | 1Mem | O/1/M |
-----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 12 (100)| | | | |
| 1 | SORT ORDER BY | | 3 | 33 | 12 (17)| 00:00:01 | 2048 | 2048 | 1/0/0|
|* 2 | HASH JOIN OUTER| | 3 | 33 | 11 (10)| 00:00:01 | 1156K| 1156K| 1/0/0|
| 3 | VIEW | | 3 | 18 | 6 (0)| 00:00:01 | | | |
| 4 | UNION-ALL | | | | | | | | |
| 5 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 | | | |
| 6 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 | | | |
| 7 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 | | | |
| 8 | VIEW | | 2 | 10 | 4 (0)| 00:00:01 | | | |
| 9 | UNION-ALL | | | | | | | | |
| 10 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 | | | |
| 11 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 | | | |
-----------------------------------------------------------------------------------------------------
with t1 as (select 1 id, 'a' val from dual union all
select 2 id, 'b' val from dual union all
select 3 id, 'c' val from dual),
t2 as (select 1 id, null val from dual union all
select 3 id, 'd' val from dual)
select *
from t1
left outer join t2 on (t1.id = t2.id)
where t2.val is not null
order by t1.id;
-----------------------------------------------------------------------------------------------------
| Id | Operation | Name | E-Rows |E-Bytes| Cost (%CPU)| E-Time | OMem | 1Mem | O/1/M |
-----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 10 (100)| | | | |
| 1 | SORT ORDER BY | | 1 | 11 | 10 (20)| 00:00:01 | 2048 | 2048 | 3/0/0|
|* 2 | HASH JOIN | | 1 | 11 | 9 (12)| 00:00:01 | 1156K| 1156K| 3/0/0|
| 3 | VIEW | | 2 | 10 | 2 (0)| 00:00:01 | | | |
| 4 | UNION-ALL | | | | | | | | |
|* 5 | FILTER | | | | | | | | |
| 6 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 | | | |
| 7 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 | | | |
| 8 | VIEW | | 3 | 18 | 6 (0)| 00:00:01 | | | |
| 9 | UNION-ALL | | | | | | | | |
| 10 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 | | | |
| 11 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 | | | |
| 12 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 | | | |
-----------------------------------------------------------------------------------------------------
Note the change from HASH JOIN OUTER to HASH JOIN in the row with id = 2 in the 2nd explain plan.

ORACLE: INSERT SELECT FROM 2 views and value from param

I'm trying to insert some fields into MYTABLE from views MYVIEW1 and MYVIEW2 and then add a value from a parameter (this is part of a stored procedure) for UPDATED_BY, SYSDATE for UPDATED_ON. How can I correctly do this with INSERT SELECT or some other way entirely?
MYVIEW1
+------+----+-----+-----------+---------+
| YR | MO | QTR | USER_CODE | MO_PERF |
+------+----+-----+-----------+---------+
| 2012 | 1 | 1 | 1099 | 89 |
| 2012 | 2 | 1 | 1099 | 86 |
| 2012 | 3 | 1 | 1099 | 95 |
+------+----+-----+-----------+---------+
MYVIEW2
+------+-----+-----------+----------+
| YR | QTR | USER_CODE | QTR_PERF |
+------+-----+-----------+----------+
| 2012 | 1 | 1099 | 90 |
+------+-----+-----------+----------+
MYTABLE
+------+-----+-----------+---------+---------+---------+---------+-------------+------------+
| YR | QTR | USER_CODE | MO1_PCT | MO2_PCT | MO3_PCT | INC | UPDATED_BY | UPDATED_ON |
+------+-----+-----------+---------+---------+---------+---------+-------------+------------+
| 2012 | 1 | 1099 | 89 | 86 | 95 | 7000 | SAMPLE NAME | 01/16/2013 |
+------+-----+-----------+---------+---------+---------+---------+-------------+------------+
INSERT INTO MYTABLE
(YR,QTR,USER_CODE,MO1_PCT,MO2_PCT,MO3_PCT,INC,UPDATED_BY,UPDATED_ON)
SELECT b.YR,b.QTR,b.USER_CODE,b.MO1_PCT,b.MO2_PCT,b.MO3_PCT,c.INC
FROM MYVIEW1 b,
MYVIEW2 c
How do I insert values for (first month of QTR's MO_PERF) as MO1_PCT and (second month of QTR's MO_PERF) as MO2_PCT and (last month of QTR's MO_PERF) as MO3_PCT, making sure that I've inserted the right month within the right quarter and year.And then check if the MO_PERF values of each month has reached at least 85, else set INC as NULL.
,CASE WHEN MO1_PCT>=85 AND MO2_PCT>=85 AND MO3_PCT>=85 THEN 7000
ELSE NULL
END INC
If you're using oracle 11g then you can use PIVOT like this:
select YR, QTR, USER_CODE, "1_MO_PCT" MO1_PCT, "2_MO_PCT" MO2_PCT, "3_MO_PCT" MO3_PCT ,
case when "1_MO_PCT" >= 85 and "2_MO_PCT" >= 85 and "2_MO_PCT" >= 85 then 7000 end INC,
user updated_by, sysdate updated_on
from (
select m1.yr, m1.mo, m1.qtr, m1.user_code, m1.mo_perf, m2.qtr_perf
from myview1 m1 join myview2 m2 on m1.yr=m2.yr
and m1.qtr = m2.qtr and m1.user_code = m2.user_code )t
pivot(
max(mo_perf) MO_PCT for mo in (1,2,3)
)
Here is a sqlfiddle demo

Resources