select rows having same column value - oracle

I am trying to form a query for following scenario
if my table is
signature | id | operation
abc | 1 | 1234
xyz | 1 | 1234
pqr | 2 | 1234
then my output should be
signature | id
abc | 1
xyz | 1
i.e. rows having same value in a particular column.
i have formed query like
select signature,id
from tablename
where operation = '1234'
group by signature,id
having count(*) >0;
but this is returning everything including xyz | 1 also.
Can someone suggest me correct query?

Try this:
SELECT signature,COUNT(id) FROM table_name WHERE operation = '1234' GROUP BY id;

I haven't tested this and it might be overly complicated but I THINK this will work:
SELECT Signature, ID from tablename WHERE ID in( SELECT ID FROM (SELECT ID, COUNT(*) as NumRecords from tablename GROUP BY ID HAVING NumRecords > 1)))

Try this:
We need to apply PARTITION on ID column as follows,
SELECT Result.[signature], Result.ID ,
ROW_NUMBER() over(PARTITION BY Result.ID ORDER BY Result.ID) AS [RowNum]
INTO #TempResult
FROM table_name AS Result
GROUP BY Result.[signature], Result.ID
SELECT Result.[signature], Result.ID
FROM #TempResult AS Result
where Result.[RowNum] = 1

You can use where exists for an alternative to group by
select
signature,
id
from tablename t1
where exists (
select * from tablename t2
where t1.id = t2.id
and t1.signature != t2.signature
and t1.operation = 1234
);

Related

Getting rows that are having distinct id with bulk collect - Oracle

I want to get the records that are having distinct member id using bulk collect.
My query is like below:
...
type members_nt is table of table2%rowtype;
members_bc members_nt;
...
select t2.* bulk collect
into members_bc
from table1 t1
,table2 t2
where t1.isn= t2.isn
and t1.customer= customer
and t1.insert_date between ... and ... );
t2 table has a column named member_id and what I want to get into members_bc is the rows having distinct member_id.
For example if my t2 table has something like this:
name | surname | account | member_id
john alby abc 123
john alby def 123
mary hale lkj 234
I want to bulk collect only
name | surname | account | member_id
john alby abc 123
mary hale lkj 234
or
name | surname | account | member_id
john alby def 123
mary hale lkj 234
It does not matter which one. But memberid must be unique in the members_bc.
How can I achieve this? Thank you.
Use the ROW_NUMBER() analytic function to give each row a number per member_id and then filter to only get the first row.
DECLARE
TYPE members_nt IS TABLE OF table2%ROWTYPE;
members_bc members_nt;
BEGIN
SELECT t2.name, t2.surname, t2.account, t2.member_id
BULK COLLECT INTO members_bc
FROM (
SELECT t2.*,
ROW_NUMBER() OVER ( PARTITION BY member_id ORDER BY ROWNUM ) AS rn
FROM table2 t2
) t2
WHERE rn = 1;
FOR i IN 1 .. members_bc.COUNT LOOP
DBMS_OUTPUT.PUT_LINE( members_bc(i).member_id || ' ' || members_bc(i).name );
END LOOP;
END;
/
outputs:
123 john
234 mary
db<>fiddle here
You can avoid "select *" and just enter the desired columns by creating a cursor for your select and then creating a %rowtype of the cursor.
Using #MT0 answer as baseline template.)
declare
cursor members_cur is
select t2.name, t2.surname, t2.account, t2.member_id
from ( select t2.name, t2.surname, t2.account, t2.member_id
, row_number() over ( partition by member_id order by rownum ) as rn
from table2 t2
) t2
where rn = 1;
type members_tt is table of members_cur%rowtype;
members_bc members_tt;
begin
open members_cur;
fetch members_cur
bulk collect into members_bc;
close members_cur;
for i in 1 .. members_bc.count
loop
dbms_output.put_line(members_bc(i).member_id || ' ' ||members_bc(i).name);
end loop;
end;
A simple option is to use aggregation, e.g.
select name,
surname,
min(account) as account, --> this
member_id
from ...
group by name, surname, member_id --> and this

rewrite query without DENSE_RANK

I have one very slow query and try to optimize response time by using a materialized view. But one part is not compatible with General Restrictions on Fast Refresh.
How to rewrite it without DENSE_RANK?
create table t (id,object_id,log_cre_date) as
select 1,2,to_date('18/5/2010, 08:00','dd/mm/yyyy, hh:mi') from dual union all
select 2,2,to_date('18/5/2010, 10:00','dd/mm/yyyy, hh mi') from dual union all
select 3,3,to_date('18/5/2010, 11:00','dd/mm/yyyy, hh mi') from dual union all
select 4,3,to_date('18/5/2010, 12:10','dd/mm/yyyy, hh mi') from dual union all
select 5,4,to_date('18/5/2010, 12:20','dd/mm/yyyy, hh mi') from dual union all
select 6,4,to_date('18/5/2010, 11:30','dd/mm/yyyy, hh mi') from dual;
SELECT
MAX(t.id) KEEP(DENSE_RANK FIRST ORDER BY log_cre_date ASC) id,
t.object_id
FROM
t
GROUP BY
t.object_id
I am not sure the accepted answer is fast refreshable. Here is a query that definitely is:
SELECT max(cast(to_char(t.log_cre_date,'YYYYMMDDHH24MISS') || lpad(t.id,30,'0') as varchar2(80))) maxid,
t.object_id,
COUNT(*) cnt
FROM t
GROUP BY t.object_id;
The idea is to append the id to the log_cre_date and take the max of the concatenation. That way, you can extract the id you need later.
So, to get the id, you would do this:
SELECT to_char(substr(maxid,-30)) id, object_id
FROM your_materialized_view;
You could put that in a view to hide the complexity.
Here is a full example:
Create the base table
DROP TABLE t;
create table t (id,object_id,log_cre_date) as
select 1,2,to_date('18/5/2010, 08:00','dd/mm/yyyy, hh:mi') from dual union all
select 2,2,to_date('18/5/2010, 10:00','dd/mm/yyyy, hh mi') from dual union all
select 3,3,to_date('18/5/2010, 11:00','dd/mm/yyyy, hh mi') from dual union all
select 4,3,to_date('18/5/2010, 12:10','dd/mm/yyyy, hh mi') from dual union all
select 5,4,to_date('18/5/2010, 12:20','dd/mm/yyyy, hh mi') from dual union all
select 6,4,to_date('18/5/2010, 11:30','dd/mm/yyyy, hh mi') from dual;
Add some constraints to allow fast-refresh MV
ALTER TABLE t MODIFY id NOT NULL;
ALTER TABLE t ADD CONSTRAINT t_pk PRIMARY KEY ( id );
Create a snapshot log to enable fast refresh
--DROP MATERIALIZED VIEW LOG ON t;
CREATE MATERIALIZED VIEW LOG ON t WITH ROWID, PRIMARY KEY (OBJECT_ID, LOG_CRE_DATE) INCLUDING NEW VALUES;
Create the materialized view (note presence of COUNT(*) in select-list. Important!
--DROP MATERIALIZED VIEW t_mv;
CREATE MATERIALIZED VIEW t_mv
REFRESH FAST ON COMMIT AS
SELECT max(cast(to_char(t.log_cre_date,'YYYYMMDDHH24MISS') || lpad(t.id,30,'0') as varchar2(80))) maxid,
t.object_id,
COUNT(*) cnt
FROM t
GROUP BY t.object_id;
Test it out
select to_number(substr(maxid,-30)) id, object_id
from t_mv;
+----+-----------+
| ID | OBJECT_ID |
+----+-----------+
| 2 | 2 |
| 4 | 3 |
| 5 | 4 |
+----+-----------+
DELETE FROM t WHERE id = 5;
COMMIT;
select to_number(substr(maxid,-30)) id, object_id
from t_mv;
+----+-----------+
| ID | OBJECT_ID |
+----+-----------+
| 4 | 3 |
| 5 | 4 |
| 1 | 2 | -- Now ID #1 is the latest for object_id 2
+----+-----------+
Maybe this query will run faster:
select object_id, id
from (
select object_id, first_value(id) over(partition by object_id order by log_cre_date) as id
from t
)
group by object_id, id;
Hope it helps!
I went through the restriction but I am not sure if following query will work or not.
Try this and let us know if it works.
Select t.id, t.object_id from
T join
(SELECT
min(log_cre_date) mindt,
t.object_id
FROM
t
GROUP BY
t.object_id) t1
On t.object_id = t1.object_id
And t.log_cre_date = t1.mindt;
Cheers!!

Order by: Special characters before ABC

I have this sql:
SELECT -1 AS ID, '(None)' AS NAME
FROM TABLE_1 WHERE ID=1
UNION
SELECT ID, NAME
FROM TABLE_2
ORDER BY 2
Table data:
ID | NAME
1 | Direct
2 | Personal
3 | Etc
So if i execute this sql in Oracle 10 it returns these:
Result:
ID | NAME
1 | Direct
3 | Etc
-1 | (None)
2 | Personal
How is it possible to sort the "(None)" always to the top?
If i use
' (None) ' as Name
instead of
'(None)' as Name
It works, because the space before the (None), but that is not a solution.
You can add a dummy column ORDER_COL and then order on that column
select ID, NAME from
(
SELECT -1 AS ID, '(None)' AS NAME, 1 as ORDER_COL FROM TABLE_1 WHERE ID=1
UNION
SELECT ID, NAME, 2 as ORDER_COL FROM TABLE_2
)
order by ORDER_COL, NAME;
Try this. NULLS LAST is the default for ascending order in Oracle. make it NULLS FIRST for '(None)'. Also, use UNION ALL as UNION removes duplicates and is less efficient.
SELECT *
FROM (
SELECT -1 AS ID
,'(None)' AS NAME
FROM TABLE_1
WHERE ID = 1
UNION ALL
SELECT ID
,NAME
FROM TABLE_2
)
ORDER BY CASE
WHEN NAME = '(None)'
THEN NULL
ELSE NAME -- or id if you want
END NULLS FIRST;

How to get mismatch records of two tables from same database in hive?

Eg:
select username, country from table1
Minus
Select username, country from table2;
The above minus query works in RDBMS but i want the same result using hive. Can we use joins here in hive to get the result? If so how to get proper result using hive query.
Set operations (MINUS/EXCEPT/INTERSECT in addition to UNION) are supported as of Hive 2.3.0 (released on 17 July 2017)
https://issues.apache.org/jira/browse/HIVE-12764
Demo
create table table1 (username string, country string);
create table table2 (username string, country string);
insert into table1 values ('Danny','USA'),('Danny','USA'),('David','UK');
insert into table2 values ('David','UK'),('Michal','France');
select username, country from table1
minus
Select username, country from table2
;
+--------------+-------------+
| _u1.username | _u1.country |
+--------------+-------------+
| Danny | USA |
+--------------+-------------+
In older Hive version you can use -
select username
,country
from ( select 1 tab,username, country from table1
union all select 2 tab,username, country from table2
) t
group by username
,country
having count(case when tab = 2 then 1 end) = 0
;
+----------+---------+
| username | country |
+----------+---------+
| Danny | USA |
+----------+---------+
You may utilize left join as follows
select table1.username, table1.country
from table1 left join table2
on table1.username=table2.username and table1.country=table2.country
where table2.username is NULL and table2.country is NULL;
Yes , As minus and exist not usually work in hive we can do minus operation by below LEFT JOIN condition.
SELECT t1.username, t1.country
FROM
(select username, country from table1) t1
LEFT JOIN
(Select username, country from table2) t2
ON t1.username =t2.username
AND t1.country =t2.country
WHERE t1.username IS NULL
IMP NOTE:Please do use WHERE CLAUSE FOR NULL Operations instead of AND after join condition this will have different results.

Using DISTINCT for specific columns

select distinct employee_id, first_name, commission_pct, department_id from
employees;
When I use the above query it results in distinct combination of all the attributes mentioned. As employee_id (being the primary key for employees) is unique, the query results in producing all the rows in the table.
I want to have a result set that has distinct combination of commission_pct and department_id. so how the query should be formed. When I tried to include the DISTINCT in the middle as
select employee_id, first_name, distinct commission_pct, department_id from
employees;
It is resulting in an error
ORA-00936-missing expression
How to form a query which results have only distinct combination of commission and department_id.The table is from HR schema of oracle.
What you request is impossible. You cannot select all the employee ids but have only distinct commission_pct and department_id.
So think it over, what you want to show:
All distinct commission_pct, department_id only?
All distinct commission_pct, department_id and the number of relevant employees?
All distinct commission_pct, department_id and the relevant employees comma separated?
All employees, but with nulls when commission_pct and department_id are the same as in the line before?
The first can be solved with DISTINCT. The second and third with GROUP BY (plus count or listagg). The last would be solved with the analytic function LAG.
You have to remove two columns before distinct
select distinct commission_pct, department_id from
employees;
Indeed, if your second query would work, what do you expect to see in the first two columns? Consider example data
| employee_id | first_name | commission_pct | department_id |
| 1 | "x" | "b" | 3 |
| 2 | "y" | "b" | 3 |
| 1 | "x" | "c" | 4 |
| 2 | "y" | "c" | 4 |
You expect to get only two row result like this
| employee_id | first_name | commission_pct | department_id |
| ? | ? | "b" | 3 |
| ? | ? | "c" | 4 |
But what do you expect in the first two column?
Can you try this one?
SELECT
NAME1,
PH
FROM
(WITH T
AS (SELECT
'mark' NAME1,
'1234567' PH
FROM
DUAL
UNION ALL
SELECT
'bailey',
'456789'
FROM
DUAL
UNION ALL
SELECT
'mark',
'987654'
FROM
DUAL)
SELECT
NAME1,
PH,
ROW_NUMBER ( ) OVER (PARTITION BY NAME1 ORDER BY NAME1) SEQ
FROM
T)
WHERE
SEQ = 1;
If you dont care on a specific row, then use aggregate functions
SELECT
NAME1,
MAX ( PH ) PH
FROM
T
GROUP BY
NAME1;

Resources