Best way to write this query - sql

I have two MySql tables as shown below with the data shown:
CREATE TABLE `A` (
`id` int(12) NOT NULL AUTO_INCREMENT,
`status` varchar(50) DEFAULT NULL,
`another_field` varchar(50) DEFAULT NULL
)
INSERT INTO `A` VALUES ('1', null, 'a');
INSERT INTO `A` VALUES ('2', null, 'b');
INSERT INTO `A` VALUES ('3', null, 'c');
CREATE TABLE `B` (
`id` int(12) NOT NULL AUTO_INCREMENT,
`status` varchar(50) DEFAULT NULL,
`tableA_id` int(12) DEFAULT NULL,
PRIMARY KEY (`id`)
)
INSERT INTO `B` VALUES ('1', 'aa', '1');
INSERT INTO `B` VALUES ('2', 'aa', '1');
INSERT INTO `B` VALUES ('3', 'aa', '2');
INSERT INTO `B` VALUES ('4', 'aa', '3');
INSERT INTO `B` VALUES ('5', 'bb', '3');
I want to know if it is possible to update A.status if all B.status are the same when A.id = B.tableA_id using a single query?
This is what I want my table A to look like:
('1', 'aa', 'a') - Status is updated to 'aa' as B.id 1 & 2 have the same status and same B.tableA_id value.
('2', 'aa', 'b') - Status is updated to 'aa' as B.id 3 has the same status.
('3', null, 'c') - This is not updated because B.id 4 & 5 have different status and the same table2.table1_id value.
Thanks

UPDATE A
SET status = COALESCE((
SELECT MAX(B.status)
FROM B
WHERE B.tableA_id = A.id
HAVING MAX(B.status) = MIN(B.status)
), A.status)
(Note: I added a correction, you need the COALESCE(..., A.status) or otherwise the status will be set to NULL in case there were multiple statuses in B

Not sure about MySql but in MSSQL you could write something like:
UPDATE A SET A.Status = 'aa'
FROM A INNER JOIN B on A.id = B.tableA_id
WHERE b.status = 'aa'
It should be similar in MySQL, but I'm not if the language supports joins on update. But still I hope it helps.

UPDATE a SET status =
(
SELECT status FROM b WHERE tableA_id = a.id LIMIT 0,1
)
WHERE id IN
(
SELECT tableA_id FROM b
GROUP BY tableA_id
HAVING COUNT(DISTINCT status) = 1
)
Update: Roland was right; I have updated the query and it now yields the correct results.

CREATE TABLE `A` (
`id` int(12) NOT NULL AUTO_INCREMENT,
`status` varchar(50) DEFAULT NULL,
`another_field` varchar(50) DEFAULT NULL
)
INSERT INTO `A` VALUES ('1', null, 'a');
INSERT INTO `A` VALUES ('2', null, 'b');
INSERT INTO `A` VALUES ('3', null, 'c');
CREATE TABLE `B` (
`id` int(12) NOT NULL AUTO_INCREMENT,
`status` varchar(50) DEFAULT NULL,
`tableA_id` int(12) DEFAULT NULL,
PRIMARY KEY (`id`)
)
INSERT INTO `B` VALUES ('1', 'aa', '1');
INSERT INTO `B` VALUES ('2', 'aa', '1');
INSERT INTO `B` VALUES ('3', 'aa', '2');
INSERT INTO `B` VALUES ('4', 'aa', '3');
INSERT INTO `B` VALUES ('5', 'bb', '3');

Related

SQL Server Insert Value that match foreign key

I use SQL Server SGBD and I have the following scenario with 2 tables :
CREATE TABLE D_CLIENT
(
ID_CLIENT varchar(10) NOT NULL,
NOM_CLIENT varchar(10) NULL,
PRIMARY KEY (ID_CLIENT)
)
CREATE TABLE F_FACT
(
ANNEE varchar(10) NOT NULL,
DOCUMENT varchar(10) NOT NULL,
NUM_DOC varchar(10) NOT NULL,
NUM_LIGNE_DOC varchar(10) NOT NULL,
ID_CLIENT varchar(10) NOT NULL,
ID_REP varchar(10) NOT NULL,
CA decimal(10,2) NULL,
PRIMARY KEY (ANNEE, DOCUMENT, NUM_DOC, NUM_LIGNE_DOC),
CONSTRAINT FK_FactClient
FOREIGN KEY (ID_CLIENT) REFERENCES D_CLIENT(ID_CLIENT)
)
INSERT INTO D_CLIENT (ID_CLIENT, NOM_CLIENT)
VALUES ('1', 'A'), ('2', 'B'), ('3', 'C'), ('4', 'D')
INSERT INTO F_FACT (ANNEE, DOCUMENT, NUM_DOC, NUM_LIGNE_DOC, ID_CLIENT, ID_REP, CA)
VALUES ('2022', 'FAC', '1', '1', '1', '1', 100),
('2022', 'FAC', '1', '2', '1', '1', 100),
('2022', 'FAC', '2', '1', '5', '1', 100)
I have a foreign key on ID_CLIENT for the integrity of data, so if I try to insert a row into F_FACT with an ID_CLIENT which doesn't exist in D_CLIENT, it will fail and it's normal because of foreign key constraint.
So when I execute the INSERT query, I get a error message because the value '5' doesn't exist in the table D_CLIENT but the 2 first row are not inserted either, where the ID_CLIENT does exist in the D_CLIENT table.
My question: is it possible, with a query, to insert only the correct rows (that's means the 2 first rows) and **reject only ** the third row ?
Thanks for your help
Join the source with the lookup table to reject missing values
with src as (
select *
from (
VALUES
('2022','FAC','1','1','1','1',100),
('2022','FAC','1','2','1','1',100),
('2022','FAC','2','1','5','1',100)
) t(ANNEE, DOCUMENT, NUM_DOC, NUM_LIGNE_DOC, ID_CLIENT, ID_REP, CA)
)
insert into F_FACT(ANNEE, DOCUMENT, NUM_DOC, NUM_LIGNE_DOC, ID_CLIENT, ID_REP, CA)
select src.ANNEE, src.DOCUMENT, src.NUM_DOC, src.NUM_LIGNE_DOC, src.ID_CLIENT, src.ID_REP, src.CA
from src
join D_CLIENT c on c.ID_CLIENT = src.ID_CLIENT
db<>fiddle
This is something I would use an exists check for:
insert into F_FACT (ANNEE, DOCUMENT, NUM_DOC, NUM_LIGNE_DOC, ID_CLIENT, ID_REP, CA)
select ANNEE, DOCUMENT, NUM_DOC, NUM_LIGNE_DOC, ID_CLIENT, ID_REP, CA from (
values
('2022','FAC','1','1','1','1',100),
('2022','FAC','1','2','1','1',100),
('2022','FAC','2','1','5','1',100)
)v(ANNEE, DOCUMENT, NUM_DOC, NUM_LIGNE_DOC, ID_CLIENT, ID_REP, CA)
where exists (select * from D_CLIENT d where d.ID_CLIENT = v.ID_CLIENT)

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;

ORDER BY a subqueries which has JOIN, LEFT JOIN and UNION

My question is: How do I order the subquery by PositionAssetId and then follow by its related PhysicalAssetId based on table TrxAssetPool?
I need LEFT JOIN because not all Position and Physical were linked together. Some of Position/Physical were standalone. A Physical might exist in PhysicalAsset and TrxPhysicalAsset but not exist in TrxAssetPool because it was not linked to any Position; and vice versa. These data also needed to be displayed.
CREATE TABLE `PositionAssets` (
`Id` int(5) unsigned NOT NULL,
`Code` varchar(50) NOT NULL,
`Desc` varchar(200) NOT NULL,
PRIMARY KEY (`Id`)
);
CREATE TABLE `PhysicalAssets` (
`Id` int(5) unsigned NOT NULL,
`Code` varchar(50) NOT NULL,
`Desc` varchar(200) NOT NULL,
PRIMARY KEY (`Id`)
);
CREATE TABLE `TrxPositionAssets` (
`Id` int(5) unsigned NOT NULL,
`MaintTrxId` int(5) unsigned NOT NULL,
`PositionAssetId` int(5) NOT NULL,
PRIMARY KEY (`Id`,`MaintTrxId`)
);
CREATE TABLE `TrxPhysicalAssets` (
`Id` int(5) unsigned NOT NULL,
`MaintTrxId` int(5) unsigned NOT NULL,
`PhysicalAssetId` int(5) NOT NULL,
PRIMARY KEY (`Id`,`MaintTrxId`)
);
CREATE TABLE `TrxAssetPool` (
`Id` int(5) unsigned NOT NULL,
`MaintTrxId` int(5) NOT NULL,
`PositionAssetId` int(5) NOT NULL,
`PhysicalAssetId` int(5) NOT NULL,
PRIMARY KEY (`Id`)
);
INSERT INTO `PositionAssets` (`Id`, `Code`, `Desc`) VALUES
('1', 'PositionC', 'Air conditioner'),
('2', 'PositionB', 'Laptop'),
('3', 'PositionA', 'Mobile Phone')
;
INSERT INTO `PhysicalAssets` (`Id`, `Code`, `Desc`) VALUES
('1', 'PhysicalD', 'Dunlop Car Tyre'),
('2', 'PhysicalA1', 'Samsung'),
('3', 'PhysicalB2', 'Acer'),
('4', 'PhysicalB1', 'Lenovo')
;
INSERT INTO `TrxPositionAssets` (`Id`, `MaintTrxId`, `PositionAssetId`) VALUES
('1', '1', '2'),
('2', '1', '3'),
('3', '1', '1')
;
INSERT INTO `TrxPhysicalAssets` (`Id`, `MaintTrxId`, `PhysicalAssetId`) VALUES
('1', '1', '2'),
('2', '1', '3'),
('3', '1', '1'),
('4', '1', '4')
;
INSERT INTO `TrxAssetPool` (`Id`,`MaintTrxId`,`PositionAssetId`,`PhysicalAssetId`) VALUES
('1', '1', '3', '2'),
('2', '1', '2', '4'),
('3', '1', '2', '3')
;
SELECT DataType, DataCode, DataDesc
FROM (
SELECT 'Position' AS DataType, pos.Code AS DataCode, pos.Desc AS DataDesc
FROM TrxPositionAssets trxpos
JOIN PositionAssets pos ON pos.Id = trxpos.PositionAssetId
LEFT JOIN TrxAssetPool trxpool ON (trxpool.PositionAssetId = trxpos.PositionAssetId and trxpool.MaintTrxId = trxpos.MaintTrxId)
WHERE trxpos.MaintTrxId = 1
UNION
SELECT 'Physical' AS DataType, phy.Code AS DataCode, phy.Desc AS DataDesc
FROM TrxPhysicalAssets trxphy
JOIN PhysicalAssets phy ON phy.Id = trxphy.PhysicalAssetId
LEFT JOIN TrxAssetPool trxpool ON (trxpool.PhysicalAssetId = trxphy.PhysicalAssetId and trxpool.MaintTrxId = trxphy.MaintTrxId)
WHERE trxphy.MaintTrxId = 1
) DataPool
Sample at sqlfiddle.com
Current result:
DataType DataCode DataDesc
Position PositionA Mobile Phone
Position PositionB Laptop
Position PositionC Air conditioner
Physical PhysicalA1 Samsung
Physical PhysicalB1 Lenovo
Physical PhysicalB2 Acer
Physical PhysicalD Dunlop Car Tyre
Expected Result:
DataType DataCode DataDesc
Position PositionA Mobile Phone
Physical PhysicalA1 Samsung
Position PositionB Laptop
Physical PhysicalB1 Lenovo
Physical PhysicalB2 Acer
Position PositionC Air conditioner
Physical PhysicalD Dunlop Car Tyre
Air conditioner is not related to any Physical. Dunlop Car Tyre is not related to any Position.
In the end of query put,
ORDER BY DATA.DataId ASC;
You need to select the information you want in the subquery. Also, the LEFT JOINs are not necessary, because they are undone by the WHERE and you probably want UNION ALL:
SELECT Data.[DataId], Data.[TrxnDataId], Data.[Type]
FROM ((SELECT pa.[Id] AS DataId, tpa.[Id] AS TrxnDataId, 'Position' AS Type,
tap.PositionAssetId, 1 as ord
FROM {TrxPositionAssets} tpa JOIN
{PositionAssets} pa
ON pa.[Id] = tpa.[PositionAssetId] JOIN
TrxAssetPool} tap
ON tap.[PositionAssetId] = pa.[Id] AND tap.[TrxId] = tpa.[TrxId])
WHERE tpa.[TrxId] = #TrxId
) UNION ALL
(SELECT pa.[Id] AS DataId, tpa.[Id] AS TrxnDataId, 'Physical' AS Type,
tap.PositionAssetId, 2 as ord
FROM {TrxPhysicalAssets} tpa JOIN
{PhysicalAssets} pa
ON pa.[Id] = tpa.[PhysicalAssetId] JOIN
{TrxAssetPool} tap
ON tap.[PhysicalAssetId] = pa.[Id] AND tap.[TrxId] = tpa.[TrxId]
WHERE tpa.[TrxId] = #TrxId
)
) data
ORDER BY PositionAssetId, ord, dataId;
To solve your problem you have to simplify it and solve it step by step, it will be easier to find a solution.
E.g. Simply join two table
SELECT Orders.OrderID, Customers.CustomerName, Orders.OrderDate
FROM Orders
INNER JOIN Customers ON Orders.CustomerID=Customers.CustomerID;

Oracle - Comparing multiple records

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');

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