oracle how to get column value from other table - sql

I have the following tables with some columns:
foo
reportdate baar
01.04.16 1
baar_value
b_value from_date to_date
1 01.01.16 01.01.17
1 01.01.15 01.01.16
The logic of what I want is a little bit complex but I am trying to find a way to reference the reportdate from table foo in baar_value-where clause in the following query:
SELECT *
FROM foo
WHERE baar IS NOT NULL
AND NOT EXISTS
(SELECT *
FROM baar_value
WHERE b_value = baar
AND reportdate
BETWEEN from_date AND to_date
)
how check if foo.reportdate is between baar_value.from_date and baar_value_to_date?

From your description maybe you simply need the following:
SELECT *
FROM foo
WHERE baar IS NOT NULL
AND NOT EXISTS
(SELECT *
FROM baar_value
WHERE b_value = baar
AND foo.reportdate BETWEEN from_date AND to_date
)
Test:
SQL> create table foo(reportdate, baar) as
2 select date '2016-04-01', 1 from dual;
Table created.
SQL> create table baar_value( b_value, from_date, to_date) as
2 select 1, date '2016-01-01', date '2017-01-01' from dual union all
3 select 1, date '2015-01-01', date '2016-01-01' from dual;
Table created.
SQL> SELECT *
2 FROM foo
3 WHERE baar IS NOT NULL
4 AND NOT EXISTS
5 (SELECT *
6 FROM baar_value
7 WHERE b_value = baar
8 AND foo.reportdate BETWEEN from_date AND to_date
9 );
no rows selected

Related

When single row not null then return nothing with Oracle SQL

When querying a table in Oracle Dev I need to ignore any ID that doesn't have a null date.
An ID can be returned multiple times e.g. 5 times, 4 can have a null date but one might have 01.01.2022 in which case all of them need to be ignored.
Here is the SQL:
SELECT
ID,
ACTIVATION_DATE,
ACCEPTED_DATE
FROM TABLE
WHERE ACCEPTED_DATE IS NOT NULL
--AND ACTIVATION_DATE IS NULL
AND ID IN ('AA1','AA2');
And the result:
ID
ACTIVATION_DATE
ACCEPTED_DATE
AA1
01/04/2022
AA1
15/03/2022
AA1
22/08/2022
07/06/2022
AA1
11/05/2022
AA2
06/06/2022
AA2
25/09/2022
12/12/2021
You can see AA1 has pulled 4 rows but because it has one activation date, they now all need to be ignored. If I leave AND ACTIVATION_DATE IS NULL in there it will still return the blank rows, which need to be ignored altogether.
Assume this will need to be a subquery? Any help would be much appreciated!
I have tried the SQL query as above
Use the COUNT analytic function:
SELECT id,
activation_date,
accepted_date
FROM (
SELECT id,
activation_date,
accepted_date,
COUNT(activation_date) OVER (PARTITION BY id) AS num_active
FROM table_name
WHERE accepted_date IS NOT NULL
AND id IN ('AA1','AA2')
)
WHERE num_active = 0;
I'd try with not exists:
Sample data:
SQL> WITH
2 table1 (id, activation_date, accepted_date)
3 AS
4 (SELECT 'AA1', NULL , DATE '2022-04-01' FROM DUAL UNION ALL
5 SELECT 'AA1', NULL , DATE '2022-03-15' FROM DUAL UNION ALL
6 SELECT 'AA1', DATE '2022-08-22', DATE '2022-06-07' FROM DUAL UNION ALL
7 SELECT 'AA2', DATE '2022-09-25', DATE '2021-12-12' FROM DUAL)
Query begins here:
8 SELECT id, activation_date, accepted_date
9 FROM table1 a
10 WHERE id IN ('AA1', 'AA2')
11 AND NOT EXISTS
12 (SELECT NULL
13 FROM table1 b
14 WHERE b.id = a.id
15 AND b.activation_date IS NULL);
ID ACTIVATION ACCEPTED_D
--- ---------- ----------
AA2 25/09/2022 12/12/2021
SQL>
AA1 is excluded from result as it contains NULL values in activation_date. AA2 is being returned as its activation_date isn't empty.

Oracle: Select rows when value in one column changes

I have the following table:
PLACE USER_ID Date
---------- ---------- -----------------------------
ABC 4 14/04/20 12:05:29,255000000
ABC 4 14/04/20 15:42:28,389000000
ABC 4 14/04/20 18:33:20,202000000
ABC 4 14/04/20 22:51:28,339000000
XYZ 4 14/04/20 11:07:23,335000000
XYZ 2 14/04/20 12:15:12,123000000
ABC 4 13/04/20 22:09:33,255000000
QWE 4 13/04/20 10:18:29,144000000
XYZ 2 14/04/20 10:05:47,255000000
And I need to get the rows when the place changes order by date for the user_id that I select.
So the desired result should be this (for user_id 4):
PLACE USER_ID DATE
---------- ---------- -----------------------------
ABC 4 14/04/20 12:05:29,255000000
XYZ 4 14/04/20 11:07:23,335000000
ABC 4 13/04/20 22:09:33,255000000
QWE 4 13/04/20 10:18:29,144000000
I tried with min date but in my example, I lose data if the user goes back to that place:
SELECT MIN(DATE), PLACE FROM user_places WHERE USER_ID=4 GROUP BY PLACE
Result I get (missing one row):
PLACE USER_ID DATE
---------- ---------- -----------------------------
XYZ 4 14/04/20 11:07:23,335000000
ABC 4 13/04/20 22:09:33,255000000
QWE 4 13/04/20 10:18:29,144000000
Thanks in advance!
In Oracle 12.1 and higher, gaps-and-islands problems like this one are an easy task for the match_recognize clause. For example:
Table setup
alter session set nls_timestamp_format = 'dd/mm/rr hh24:mi:ss,ff';
create table user_places (place, user_id, date_) as
select 'ABC', 4, to_timestamp('14/04/20 12:05:29,255000000') from dual union all
select 'ABC', 4, to_timestamp('14/04/20 15:42:28,389000000') from dual union all
select 'ABC', 4, to_timestamp('14/04/20 18:33:20,202000000') from dual union all
select 'ABC', 4, to_timestamp('14/04/20 22:51:28,339000000') from dual union all
select 'XYZ', 4, to_timestamp('14/04/20 11:07:23,335000000') from dual union all
select 'XYZ', 2, to_timestamp('14/04/20 12:15:12,123000000') from dual union all
select 'ABC', 4, to_timestamp('13/04/20 22:09:33,255000000') from dual union all
select 'QWE', 4, to_timestamp('13/04/20 10:18:29,144000000') from dual union all
select 'XYZ', 2, to_timestamp('14/04/20 10:05:47,255000000') from dual
;
commit;
Query and output
select place, user_id, date_
from (select * from user_places where user_id = 4)
match_recognize (
order by date_
all rows per match
pattern (a {- b* -} )
define b as place = a.place
)
order by date_ desc -- if needed
;
PLACE USER_ID DATE_
----- ------- ---------------------------
ABC 4 14/04/20 12:05:29,255000000
XYZ 4 14/04/20 11:07:23,335000000
ABC 4 13/04/20 22:09:33,255000000
QWE 4 13/04/20 10:18:29,144000000
A few things to note here:
DATE is a reserved keyword. Not a good column name. I used DATE_
instead; notice the trailing underscore.
I hardcoded the value 4. Of course, the better practice is to make that into a bind variable.
If you really only need to do this for one user_id at a time, it is most efficient to do what I did - filter the rows first, in a subquery. However, if you need to do this for all user id's in the same query, you don't need a subquery; you select from the table itself, and you need to add partition by user_id right at the top of the match_recognize clause, before order by date_.
You can use lag() in a subquery to retrieve the "previous" place, and then filter on rows where the previous place is different that the current place:
select place, user_id, date
from (
select t.*, lag(place) over(partition by user_id order by date) lag_place
from mytable t
) t
where lag_place is null or place <> lag_place
This gives you the expected output for all users. If you want only for user 4, then you can filter in the subquery (and there is no need to partition by user):
select place, user_id, date
from (
select t.*, lag(place) over(order by date) lag_place
from mytable t
where user_id = 4
) t
where lag_place is null or place <> lag_place

compare one row with multiple rows

Ex: I have other main table which is having below data
Create table dbo.Main_Table
(
ID INT,
SDate Date
)
Insert Into dbo.Main_Table Values (1,'01/02/2018')
Insert Into dbo.Main_Table Values (2,'01/30/2018')
Create table dbo.test
(
ID INT,
SDate Date
)
Insert Into dbo.test Values (1,'01/01/2018')
Insert Into dbo.test Values (1,'01/02/2018')
Insert Into dbo.test Values (1,'01/30/2018')
Insert Into dbo.test Values (2,'10/01/2018')
Insert Into dbo.test Values (2,'01/02/2018')
Insert Into dbo.test Values (2,'01/30/2018')
I would like to compare data in main table data with test table. We have to join based on ID and if date match found then "yes" else "No". We have to compare one row with multiple rows.
Please let me know if any questions , thanks for you;re help
Something like this?
SQL> with main_table (id, sdate) as
2 (select 1, date '2018-01-02' from dual union all
3 select 2, date '2018-01-30' from dual union all
4 select 3, date '2018-07-25' from dual
5 ),
6 test_table (id, sdate) as
7 (select 1, date '2018-01-02' from dual union all
8 select 2, date '2018-08-30' from dual
9 )
10 select m.id,
11 m.sdate,
12 case when m.sdate = t.sdate then 'yes' else 'no' end status
13 from main_table m left join test_table t on t.id = m.id
14 order by m.id;
ID SDATE STATUS
---------- -------- ------
1 02.01.18 yes
2 30.01.18 no
3 25.07.18 no
SQL>
[EDIT, after reading the comment - if you find a match, you don't need that ID at all]
Here you are:
SQL> with test (id, sdate) as
2 (select 1, date '2018-01-01' from dual union all
3 select 1, date '2018-01-02' from dual union all
4 select 1, date '2018-01-30' from dual union all
5 --
6 select 2, date '2018-10-01' from dual union all
7 select 2, date '2018-01-02' from dual union all
8 select 2, date '2018-01-30' from dual
9 )
10 select id, sdate
11 from test t
12 where not exists (select null
13 from test t1
14 where t1.id = t.id
15 and t1.sdate = to_date('&par_sdate', 'yyyy-mm-dd'));
Enter value for par_sdate: 2018-01-01
ID SDATE
---------- ----------
2 2018-01-30
2 2018-01-02
2 2018-10-01
SQL> /
Enter value for par_sdate: 2018-01-02
no rows selected
SQL>

Oracle: how to query some columns as rows

I use Oracle 11 XE and have the following table:
CREATE TABLE tst
(val_a NUMBER,
val_b NUMBER,
val_c NUMBER,
val_sum NUMBER,
id NUMBER,
dt DATE)
Some sample data:
INSERT INTO tst
VALUES(12,15,17,44,1,TO_DATE('2018-03-01 00:00:00', 'YYYY-MM-DD HH24:MI:SS'));
INSERT INTO tst
VALUES(14,16,11,41,1,TO_DATE('2018-03-03 00:00:00', 'YYYY-MM-DD HH24:MI:SS'));
INSERT INTO tst
VALUES(6,7,8,21,2,TO_DATE('2018-03-01 00:00:00', 'YYYY-MM-DD HH24:MI:SS'));
I need to specify two dates and get the following result (NEW_VAL are values SUM, A, B and C for ID=1 and DT=2018-03-03, OLD_VAL are values for ID=1 and DT=2018-03-01):
ID X NEW_VAL OLD_VAL
--- --- --------- --------
1 SUM 41 44
A 14 12
B 16 15
C 11 17
Below is the query I've implemented:
select id, x, new_val, old_val from(
select tst_new.id id0, 1, tst_new.id, 'SUM' x, tst_new.val_sum new_val, tst_old.val_sum old_val from tst tst_new,
(select * from tst where dt=to_date('01.03.2018', 'dd.mm.yyyy')) tst_old
where tst_new.dt=to_date('03.03.2018', 'dd.mm.yyyy') and tst_new.id = tst_old.id and tst_new.id = 1
UNION ALL
select tst_new.id, 2, null, 'A', tst_new.val_a, tst_old.val_a from tst tst_new,
(select * from tst where dt=to_date('01.03.2018', 'dd.mm.yyyy')) tst_old
where tst_new.dt=to_date('03.03.2018', 'dd.mm.yyyy') and tst_new.id = tst_old.id and tst_new.id = 1
UNION ALL
select tst_new.id, 3, null, 'B', tst_new.val_b, tst_old.val_b from tst tst_new,
(select * from tst where dt=to_date('01.03.2018', 'dd.mm.yyyy')) tst_old
where tst_new.dt=to_date('03.03.2018', 'dd.mm.yyyy') and tst_new.id = tst_old.id and tst_new.id = 1
UNION ALL
select tst_new.id, 4, null, 'C', tst_new.val_c, tst_old.val_c from tst tst_new,
(select * from tst where dt=to_date('01.03.2018', 'dd.mm.yyyy')) tst_old
where tst_new.dt=to_date('03.03.2018', 'dd.mm.yyyy') and tst_new.id = tst_old.id and tst_new.id = 1
order by 1, 2
)
It does provide required result but looks terrible. Is there any way to get that result easier?
Also, if there is no data for the particular date, result should contain ID, X and empty cells. My query just returns nothing if there is no data for any of two dates. How to make query return empty cells if there are no values for that date?
UPDATE: I've seen examples with pivot, but in my case not only columns as rows is required, but also querying data from the same table for different dates. Also, it's not clear how to get empty cells if there is no date for the particular date.
Inner subquery is result of unpivot, outer - pivoting back:
select *
from (select to_char(dt, 'dd.mm.yyyy') dt, vals, dt_vals from tst
unpivot (dt_vals for vals in (val_a, val_b, val_c, val_sum)))
pivot (sum(dt_vals) for dt in ('01.03.2018', '03.03.2018'))
order by 1
VALS '01.03.2018' '03.03.2018'
------- ------------ ------------
VAL_A 18 14
VAL_B 22 16
VAL_C 25 11
VAL_SUM 65 41
Next, you need to specify the rule how to filter these values:
NEW_VAL are values SUM, A B and C for ID = 1 and DT = 2018-03-03, OLD_VAL are values for ID = 1 and DT = 2018-03-01
I just hardcoded it "as is":
select *
from (select to_char(dt, 'dd.mm.yyyy') dt, vals, dt_vals from tst
unpivot (dt_vals for vals in (val_a, val_b, val_c, val_sum))
where id = 1
)
pivot (sum(dt_vals) for dt in ('01.03.2018', '03.03.2018'))
order by 1
VALS '01.03.2018' '03.03.2018'
------- ------------ ------------
VAL_A 12 14
VAL_B 15 16
VAL_C 17 11
VAL_SUM 44 41

Return all rows when one value is hit

I have 2 columns, id and date. If the date is in 2/3/16, 2/4/16, 2/5/16, I want to return all values. Hard to explain so here's an example. The following should return all 3 rows for id 1 and none of 2. Any help would be appreciated. Thanks!
id date
1 2/1/16
1 2/2/16
1 2/3/16
2 2/11/16
2 2/12/16
Try this:
SELECT id, [date]
FROM mytable
WHERE id IN (SELECT Id
FROM mytable
WHERE [date] IN ('2016-03-02', '2016-04-02', '2016-05-02'))
This is Oracle syntax; try to rewrite it for SQL Server:
WITH T AS (
SELECT 1 AS ID, TO_DATE('2/1/16', 'DD/MM/YY') AS MY_DATE FROM DUAL UNION ALL
SELECT 1 AS ID, TO_DATE('2/2/16', 'DD/MM/YY') AS MY_DATE FROM DUAL UNION ALL
SELECT 1 AS ID, TO_DATE('2/3/16', 'DD/MM/YY') AS MY_DATE FROM DUAL UNION ALL
SELECT 2 AS ID, TO_DATE('2/11/16', 'DD/MM/YY') AS MY_DATE FROM DUAL UNION ALL
SELECT 2 AS ID, TO_DATE('2/12/16', 'DD/MM/YY') AS MY_DATE FROM DUAL
)
SELECT *
FROM T T1 WHERE EXISTS(
SELECT * FROM T T2
WHERE
T1.ID = T2.ID
AND T2.MY_DATE IN (TO_DATE('2/3/16', 'DD/MM/YY'), TO_DATE('2/4/16', 'DD/MM/YY'), TO_DATE('2/5/16', 'DD/MM/YY'))
);