Oracle - Comparing multiple records - sql

I am stuck with a strange scenario now.
I have a table with many records. A few of which looks like:
Table 1:
----------------------------------------------
M_ID ROLE_ID
----------------------------------------------
idA adVA12^~^dsa25
idA adsf32^~^123^~^asdf32
idA hdghf45
idB fdngfhlo43^~^
idB pnsdfmg23
idC 123ghaskfdnk
idC hafg32^~^^~^gasdfg
----------------------------------------------
and Table 2:
-----------------------------------------------------------
ROLE_ID ADDR1 ADDR2 ADDR3
-----------------------------------------------------------
adVA12^~^dsa25 18 ben street
adsf32^~^123^~^asdf32 24 naruto park
hdghf45 18 ben street
fdngfhlo43^~^ 40 spartan ave
pnsdfmg23 40 spartan ave
123ghaskfdnk 14 southpark ave
hafg32^~^^~^gasdfg 88 brooks st
-----------------------------------------------------------
I have these tables linked by ROLE_ID.
My requirement is that, all the ROLE_IDs of a single M_ID in Table 1 must be compared for their address fields in Table 2. In case if the address of all the ROLE_IDs corresponding to that single M_ID is not the same in Table 2, it should be returned.
i.e., in this case, my result should be:
-----------------------------
M_ID ROLE_ID
-----------------------------
idA adVA12^~^dsa25
idA adsf32^~^123^~^asdf32
idA hdghf45
-----------------------------
the M_ID, and the corresponding ROLE_IDs.
I have no idea on how to compare multiple records.

I'd join the tables and count the distinct number of addresses:
SELECT m_id, role_id
FROM (SELECT t1.m_id AS m_id,
t1.role_id AS role_id,
COUNT(DISTINCT t2.addr1 || '-' || t2.addr2 || '-' || t2.addr3)
OVER (PARTITION BY t1.m_id) AS cnt
FROM t1
JOIN t2 ON t1.role_id = t2.role_id) t
WHERE cnt > 1

Slightly different approach:
SELECT t1.m_id, substr(sys.stragg(',' || t1.role_id),2) Roles,
t2.ADDR1, t2.ADDR2, t2.ADDR3
FROM t1
JOIN t2 ON t1.role_id = t2.role_id
GROUP BY t1.m_id, t2.ADDR1, t2.ADDR2, t2.ADDR3;
"M_ID" "ROLES" "ADDR1" "ADDR2" "ADDR3"
"A" "A1,A2,A3,A5,A6,A7" "1" "2" "3"
"A" "A4" "4" "2" "3"
You could add HAVING COUNT(0) > 1 to get all with matches, or use HAVING COUNT(0) = 1 to get all instances that are only used once, or use it without a HAVING to get a summary.
I used the following test data:
CREATE TABLE TEST_ROLE (
ID INTEGER PRIMARY KEY,
M_ID VARCHAR2(32) NOT NULL,
ROLE_ID VARCHAR2(256) NOT NULL
);
CREATE TABLE TEST_ROLE_ADDRESS (
ROLE_ID VARCHAR2(256) NOT NULL,
ADDR1 VARCHAR2(1000),
ADDR2 VARCHAR2(1000),
ADDR3 VARCHAR2(1000)
);
INSERT INTO TEST_ROLE VALUES(1, 'A', 'A1');
INSERT INTO TEST_ROLE VALUES(2, 'A', 'A2');
INSERT INTO TEST_ROLE VALUES(3, 'A', 'A3');
INSERT INTO TEST_ROLE VALUES(4, 'A', 'A4');
INSERT INTO TEST_ROLE VALUES(5, 'A', 'A5');
INSERT INTO TEST_ROLE VALUES(6, 'A', 'A6');
INSERT INTO TEST_ROLE VALUES(7, 'A', 'A7');
INSERT INTO TEST_ROLE_ADDRESS VALUES ('A1', '1', '2', '3');
INSERT INTO TEST_ROLE_ADDRESS VALUES ('A2', '1', '2', '3');
INSERT INTO TEST_ROLE_ADDRESS VALUES ('A3', '1', '2', '3');
INSERT INTO TEST_ROLE_ADDRESS VALUES ('A4', '4', '2', '3');
INSERT INTO TEST_ROLE_ADDRESS VALUES ('A5', '1', '2', '3');
INSERT INTO TEST_ROLE_ADDRESS VALUES ('A6', '1', '2', '3');
INSERT INTO TEST_ROLE_ADDRESS VALUES ('A7', '1', '2', '3');

Related

get only records which have similar values in column A and different values in column B

My dataset has 2 tables:
animals with animal_id and animal_type
owners with animal_id and owner_name
I want to get records only for those animals (+ their owners name) which owners have a CAT and another different pet.
Here is my schema:
CREATE TABLE IF NOT EXISTS `animals` (
`animal_id` int(6) unsigned NOT NULL,
`animal_type` varchar(200) NOT NULL,
PRIMARY KEY (`animal_id`)
) DEFAULT CHARSET=utf8;
INSERT INTO `animals` (`animal_id`, `animal_type`) VALUES
('1', 'cat'),
('2', 'dog'),
('3', 'cat'),
('4', 'cat'),
('5', 'dog'),
('6', 'dog'),
('7', 'cat'),
('8', 'dog'),
('9', 'cat'),
('10', 'hamster');
CREATE TABLE IF NOT EXISTS `owners` (
`animal_id` int(6) unsigned NOT NULL,
`owner_name` varchar(200) NOT NULL,
PRIMARY KEY (`animal_id`)
) DEFAULT CHARSET=utf8;
INSERT INTO `owners` (`animal_id`, `owner_name`) VALUES
('1', 'CatOwner'),
('2', 'DogOwner'),
('3', 'CatsOwner'),
('4', 'CatsOwner'),
('5', 'DogsOwner'),
('6', 'DogsOwner'),
('7', 'CatDogOwner'),
('8', 'CatDogOwner'),
('9', 'CatHamsterOwner'),
('10', 'CatHamsterOwner');
I can filter and show only records for owners which have more then one pet:
SELECT *
FROM animals AS a
JOIN owners AS o
ON a.animal_id = o.animal_id
WHERE o.owner_name IN (SELECT o.owner_name
FROM animals AS a
JOIN owners AS o
ON a.animal_id = o.animal_id
GROUP BY o.owner_name HAVING COUNT(o.owner_name) > 1)
Please tell me how can I make it this way:
I would suggest window functions:
SELECT ao.*
FROM (SELECT a.*, o.owner_name,
SUM(CASE WHEN a.animal_type = 'cat' THEN 1 ELSE 0 END) OVER (PARTITION BY o.owner_name) as num_cats,
COUNT(*) OVER (PARTITION BY o.owner_name) as num_animals
FROM owners o JOIN
animals a
ON a.animal_id = o.animal_id
) ao
WHERE num_cats > 0 AND num_animals >= 2;
Note: I'm not clear if the condition is for more than one animal or an animal that is not a cat. If the latter, then use:
SELECT ao.*
FROM (SELECT a.*, o.owner_name,
SUM(CASE WHEN a.animal_type = 'cat' THEN 1 ELSE 0 END) OVER (PARTITION BY o.owner_name) as num_cats,
COUNT(*) OVER (PARTITION BY o.owner_name) as num_animals
FROM owners o JOIN
animals a
ON a.animal_id = o.animal_id
) ao
WHERE num_cats > 0 AND num_animals <> num_cats;

Need to create rows out of a column in a specific way

CREATE TABLE STORE
(
STORE_CODE INT,
STORE_NAME VARCHAR(20),
STORE_YTD_SALES NUMERIC,
REGION_CODE INT,
EMP_CODE INT
);
INSERT INTO STORE
VALUES ('1', 'Access Junction', '1003455.76', '2', '8'),
('2', 'Database Corner', '1421987.39', '2', '12'),
('3', 'Tuple Charge', '986783.22', '1', '7'),
('4', 'Attribute Alley', '944568.56', '2', '3'),
('5', 'Primary Key Point', '2930098.45', '1', '15');
CREATE TABLE REGION
(
REGION_CODE INT,
REGION_DESCRIPT VARCHAR(10)
);
INSERT INTO REGION
VALUES ('1', 'East'), ('2', 'West');
I am new to SQL I need to make a list of all stores and regions, as in the following sample:
Code Description
----------- --------------------
1 Access Junction
1 East
2 Database Corner
but I am not sure how to make it work. Can someone help me, please?
Solution for your problem:
SELECT Code, Description
FROM (
SELECT STORE_CODE AS Code, Store_Name AS Description
FROM Store
UNION ALL
SELECT REGION_CODE AS Code, REGION_DESCRIPT AS Description
FROM REGION
) AS t
ORDER BY Code,Description
OUTPUT:
Code Description
---------------------------
1 Access Junction
1 East
2 Database Corner
2 West
3 Tuple Charge
4 Attribute Alley
5 Primary Key Point
Link To the Demo:
http://sqlfiddle.com/#!18/6cc1a/2

Need Help on Sql Query - how to get the recently deleted records

Please help me I am new to SQL
Could you please let me know how to get the recently deleted records from the below Table. Here I need to make a query only on rows which has more than 1 rows for the same Number column
I need Result like this :-
I need Result like this :-
ID FName LName Number CreateDate
2 BBBBB B 111111 06-26-2016 01:18:000
3 CCCCC C 333333 06-25-2016 06:10:000
4 DDDDD D 444444 06-25-2016 06:10:000
5 EEEEE E 555555 06-25-2016 23:10:000
7 FFFFF F 777777 06-26-2016 00:01:000
8 GGGGG G 888888 06-26-2016 16:01:000
9 HHHHH H 999999 06-26-2016 23:01:000
Create Table Users1
(
ID int,
FName varchar (50),
LName Varchar (50),
Number varchar(10),
CreateDate Datetime
)
INSERT INTO Users1 Values (1,'AAAA','A','11111','06-25-2016 00:10:765')
INSERT INTO Users1 Values (2,'AAAA','A','11111','06-26-2016 01:18:000')
INSERT INTO Users1 Values (3,'CCCC','C','33333','06-25-2016 06:10:000')
INSERT INTO Users1 Values (4,'DDDD','D','44444','06-25-2016 06:10:000')
INSERT INTO Users1 Values (5,'EEEE','E','55555','06-25-2016 23:10:000')
INSERT INTO Users1 Values (6,'CCCC','C','33333','06-25-2016 00:01:000')
INSERT INTO Users1 Values (7,'FFFF','F','77777','06-26-2016 00:01:000')
INSERT INTO Users1 Values (8,'GGGG','G','88888','06-26-2016 16:01:000')
INSERT INTO Users1 Values (9,'HHHH','H','99999','06-26-2016 23:01:000')
With Users1 As (
SELECT * FROM (
VALUES
(1, 'AAAA', 'A', '11111', '06-25-2016 00:10:765'),
(2, 'AAAA', 'A', '11111', '06-26-2016 01:18:000'),
(3, 'CCCC', 'C', '33333', '06-25-2016 06:10:000'),
(4, 'DDDD', 'D', '44444', '06-25-2016 06:10:000'),
(5, 'EEEE', 'E', '55555', '06-25-2016 23:10:000'),
(6, 'CCCC', 'C', '33333', '06-25-2016 00:01:000'),
(7, 'FFFF', 'F', '77777', '06-26-2016 00:01:000'),
(8, 'GGGG', 'G', '88888', '06-26-2016 16:01:000'),
(9, 'HHHH', 'H', '99999', '06-26-2016 23:01:000')
) V (ID, FName, LName, Number, CreateDate)
), Users1WithVersionNumber As (
Select *
, row_number() Over (Partition By Number Order By CreateDate DESC) As VersionNumber
From Users1
)
Select FName, LName, Number, CreateDate
From Users1WithVersionNumber
Where VersionNumber = 1 --< Take the latest version only
AND Number In (
Select Number
From Users1WithVersionNumber
Where VersionNumber = 2 --< There is at least one other version
)
SELECT CreateDate,FName,LName,Number FROM Users1
WHERE CreateDate in ( SELECT MAX(CreateDate) from Users1 group by Number)

Finding duplicate members in a table

I have come across a problem in writing a query to find duplicate members in a table. I have tried to simplify the problem with a sample table and data.
CREATE TABLE MYTABLE (
S_ID VARCHAR2(10),
PARAM VARCHAR2(10),
VALUE VARCHAR2(10)
);
INSERT INTO MYTABLE (S_ID, PARAM, VALUE) VALUES ('1', 'NAME', 'A');
INSERT INTO MYTABLE (S_ID, PARAM, VALUE) VALUES ('1', 'AGE', '15');
INSERT INTO MYTABLE (S_ID, PARAM, VALUE) VALUES ('1', 'SEX', 'M');
INSERT INTO MYTABLE (S_ID, PARAM, VALUE) VALUES ('2', 'NAME', 'B');
INSERT INTO MYTABLE (S_ID, PARAM, VALUE) VALUES ('2', 'AGE', '16');
INSERT INTO MYTABLE (S_ID, PARAM, VALUE) VALUES ('2', 'SEX', 'M');
INSERT INTO MYTABLE (S_ID, PARAM, VALUE) VALUES ('3', 'NAME', 'A');
INSERT INTO MYTABLE (S_ID, PARAM, VALUE) VALUES ('3', 'AGE', '15');
INSERT INTO MYTABLE (S_ID, PARAM, VALUE) VALUES ('3', 'SEX', 'M');
Here items with S_ID 1 and 3 are same.
Here's an easy way to find duplicate values in one field:
SELECT fieldName FROM TableName
GROUP BY FieldName
HAVING COUNT(*) > 1
You mean this? :
select colname, count(colname) from TableName
Group by colname Having (count(colname) > 1)

How to copy in field if query returns it blank?

I am writing query
from two different tables.
Table A and Table B
Here is Query.
select
A.OUT_NUM,
A.TIMESTAMP,
A.LAST_name,
A.event_type,
A.comments,
B.name
from TABLEA A
left outer join TABLEB B ON A.feed_id = B.id
where A.OUT_NUM = '12345'
and A.event_type In ('cause','status')
B.NAME is not null when event_type = xyz else it will be null
I only want to see when event_type in ('CAUSE','STATUS') but also want to see name field but not empty.
second table is what I am trying to achieve.
Thanks
Making some assumptions about your data as in comments, particularly about how to match and pick a substitute name value; and with some dummy data that I think matches yours:
create table tablea(out_num number,
equip_name varchar2(5),
event_type varchar2(10),
comments varchar2(10),
timestamp date, feed_id number);
create table tableb(id number, name varchar2(10));
alter session set nls_date_format = 'MM/DD/YYYY HH24:MI';
insert into tablea values (12345, null, 'abcd', null, to_date('02/11/2013 11:12'), 1);
insert into tablea values (12345, null, 'abcd', null, to_date('02/11/2013 11:11'), 1);
insert into tablea values (12345, null, 'abcd', null, to_date('02/11/2013 11:06'), 1);
insert into tablea values (12345, null, 'abcd', null, to_date('02/11/2013 11:06'), 1);
insert into tablea values (12345, null, 'SUB', null, to_date('02/11/2013 11:11'), 2);
insert into tablea values (12345, null, 'SUB', null, to_date('02/11/2013 11:12'), 2);
insert into tablea values (12345, null, 'XYZ', null, to_date('02/11/2013 11:13'), 3);
insert into tablea values (12345, null, 'XYZ', null, to_date('02/11/2013 11:13'), 3);
insert into tablea values (12345, null, 'XYZ', null, to_date('02/11/2013 11:13'), 3);
insert into tablea values (12345, null, 'XYZ', null, to_date('02/11/2013 11:13'), 3);
insert into tablea values (12345, null, 'XYZ', null, to_date('02/11/2013 11:13'), 3);
insert into tablea values (12345, null, 'XYZ', null, to_date('02/11/2013 11:03'), 3);
insert into tablea values (12345, null, 'CAUSE', 'APPLE', to_date('02/11/2013 11:13'), 4);
insert into tablea values (12345, null, 'CAUSE', 'APPLE', to_date('02/11/2013 11:13'), 4);
insert into tablea values (12345, null, 'CAUSE', 'APPLE', to_date('02/11/2013 11:13'), 4);
insert into tablea values (12345, null, 'STATUS', 'BOOKS', to_date('02/11/2013 11:13'), 5);
insert into tablea values (12345, null, 'STATUS', 'BOOKS', to_date('02/11/2013 11:13'), 5);
insert into tablea values (12345, null, 'STATUS', 'BOOKS', to_date('02/11/2013 11:03'), 5);
insert into tableb values(3, 'LION');
This gets your result:
select * from (
select a.out_num,
a.timestamp,
a.equip_name,
a.event_type,
a.comments,
coalesce(b.name,
first_value(b.name)
over (partition by a.out_num
order by b.name nulls last)) as name
from tablea a
left outer join tableb b on a.feed_id = b.id
where a.out_num = '12345'
and a.event_type in ('CAUSE', 'STATUS', 'XYZ')
)
where event_type in ('CAUSE', 'STATUS');
OUT_NUM TIMESTAMP EQUIP_NAME EVENT_TYPE COMMENTS NAME
---------- ------------------ ---------- ---------- ---------- ----------
12345 02/11/2013 11:03 STATUS BOOKS LION
12345 02/11/2013 11:13 STATUS BOOKS LION
12345 02/11/2013 11:13 STATUS BOOKS LION
12345 02/11/2013 11:13 CAUSE APPLE LION
12345 02/11/2013 11:13 CAUSE APPLE LION
12345 02/11/2013 11:13 CAUSE APPLE LION
The inner query includes XYZ and uses the analytic first_value() function to pick a name if the directly matched value is null - the coalesce may not be necessary if there really will never be a direct match. (You might also need to adjust the partition by or order by clauses if the assumptions are wrong). The outer query just strips out the XYZ records since you don't want those.
If you want to get a name value from any matching record then just remove the filter in the inner query.
But now you're perhaps more likely to have more than one non-null record; this will give you one that matches a.feed_id if it exists, or the 'first' one (alphabetically, ish) for that out_num if it doesn't. You could order by b.id instead, or any other column in tableb; ordering by anything in tablea would need a different solution. If you'll only have one possible match anyway then it doesn't really matter and you can leave out the order by, though it's better to have it anyway.
If I add some more data for a different out_num:
insert into tablea values (12346, null, 'abcd', null, to_date('02/11/2013 11:11'), 1);
insert into tablea values (12346, null, 'SUB', null, to_date('02/11/2013 11:12'), 2);
insert into tablea values (12346, null, 'XYZ', null, to_date('02/11/2013 11:13'), 6);
insert into tablea values (12346, null, 'CAUSE', 'APPLE', to_date('02/11/2013 11:14'), 4);
insert into tablea values (12346, null, 'STATUS', 'BOOKS', to_date('02/11/2013 11:15'), 5);
insert into tableb values(1, 'TIGER');
...then this - which just has the filter dropped, and I've left out the coalesce this time - gives the same answer for 12345, and this for 12346:
select * from (
select a.out_num,
a.timestamp,
a.equip_name,
a.event_type,
a.comments,
first_value(b.name)
over (partition by a.out_num
order by b.name nulls last) as name
from tablea a
left outer join tableb b on a.feed_id = b.id
)
where out_num = '12346'
and event_type in ('CAUSE', 'STATUS');
OUT_NUM TIMESTAMP EQUIP_NAME EVENT_TYPE COMMENTS NAME
---------- ------------------ ---------- ---------- ---------- ----------
12346 02/11/2013 11:14 CAUSE APPLE TIGER
12346 02/11/2013 11:15 STATUS BOOKS TIGER
... where TIGER is linked to abcd, not XYZ.
Use NVL() and LAG() functions.
General example using my sample data. This query fills out blank rows with data - see first_exam and last_exam columns:
SELECT id, name, proc_date, proc_type, first_exam_date
, NVL(prev_exam_date, LAG(prev_exam_date) OVER (ORDER BY name, proc_date)) last_exam_date
FROM
(
SELECT id, name, proc_date, proc_type, first_exam_date
, NVL(first_exam_date, LAG(first_exam_date) OVER (ORDER BY name, proc_date) ) prev_exam_date
FROM
(
SELECT id
, name
, proc_date
, proc_type
, (SELECT MIN(proc_date) OVER (PARTITION BY name, proc_date)
FROM stack_test WHERE proc_type LIKE 'Exam%' AND a.id = id
) first_exam_date
FROM stack_test a
));
ID NAME PROC_DATE PROC_TYPE FIRST_EXAM_DATE LAST_EXAM_DATE
--------------------------------------------------------------------------
1 George 1/1/2013 ExamA 1/1/2013 1/1/2013
2 George 1/3/2013 TreatmentA 1/1/2013
3 George 1/5/2013 TreatmentB 1/1/2013
4 George 2/1/2013 ExamB 2/1/2013 2/1/2013
5 George 2/5/2013 TreatmentA 2/1/2013