I need some help in fixing a data aberration. I create a view based on two tables with Left Join and the result has some duplicates (as given in the logic section)
Data Setup:
*******************
TEST1
*******************
PRODUCT VALUE1 KEY
1 2 12
1 3 13
1 4 14
1 5 15
*******************
TEST2
*******************
KEY ATTRIBUTE
12 DESC
13 (null)
14 DESC
15 (null)
What I tried so far
SELECT
B.KEY,
B.ATTRIBUTE,
A.PRODUCT
A.VALUE1
FROM TEST2 B LEFT JOIN TEST1 A ON TEST2.KEY = TEST1.KEY;
What I get with above SQL is
KEY ATTRIBUTE PRODUCT VALUE1
12 DESC 1 2
13 (null) 1 3
14 DESC 1 4
15 (null) 1 5
What I need to get
KEY ATTRIBUTE PRODUCT VALUE1
12 DESC 1 2
13 DESC 1 3
14 DESC 1 4
15 DESC 1 5
Logic:
Since all products with id 1 are same, I need to retain the attributes if it is NULL. So doing a distinct of PRODUCT and ATTRIBUTE will always have 1 row per product id. Test1 has more than 100 products and Test2 has corresponding descriptions.
Note: This is not a normalized design since it is data warehousing. So no complaints on design please
I would like to have a CASE statement in the attribute field.
CASE
WHEN ATTRIBUTE IS NULL THEN {fix goes here}
ELSE ATTRIBUTE
END AS ATTRIBUTE
Some one needs to see fiddle, then go here
It's not clear but if you say that for each product can be only one attribute then try to use MAX() OVER
SELECT
TEST1.Product,
TEST1.value1,
TEST2.KEY,
MAX(ATTRIBUTE) OVER (PARTITION BY test1.Product) ATTR
FROM TEST2
LEFT JOIN
TEST1 ON TEST2.KEY = TEST1.KEY
SQLFiddle demo
SQL Fiddle:
SELECT B.KEY,
CASE WHEN B.ATTRIBUTE IS NULL THEN
(
SELECT s2.ATTRIBUTE
FROM test2 s2
LEFT JOIN TEST1 s1 ON s1.KEY = s2.KEY
WHERE s1.PRODUCT = A.PRODUCT
AND s2.ATTRIBUTE IS NOT NULL
AND ROWNUM = 1
) ELSE B.ATTRIBUTE END AS ATTRIBUTE,
A.PRODUCT, A.VALUE1
FROM TEST2 B
LEFT JOIN TEST1 A ON A.KEY = B.KEY;
SELECT
NVL(attribute,'DESC')
FROM TEST2 LEFT JOIN TEST1 ON TEST2.KEY = TEST1.KEY;
Just seen its Oracle please try above
Related
I have 2 tables , one stores IDs and another logs for each ID , i would like to get sum of log for each ID and ID number from these 2 tables
A B
------- -------------
ID ID_C LOG
1 1 15
2 1 30
3 4 44
4 2 14
5 3 88
3 10
2 10
for getting sum query is
SELECT SUM(LOG) FROM B WHERE ID_C ='2' ;
notice ID and ID_C are same but name is different in tables
and for getting all ids available query is
SELECT ID FROM A ;
I would like to get the following table result
result
--------------------
ID SUM
1 45
4 44
2 24
3 98
I tried
SELECT SUM(LOG) FROM B WHERE ID_C in (SELECT ID FROM A ) ;
but it result in sum of all IDs
It looks like you just need a join aggregation here:
SELECT a.ID, SUM(b.LOG) AS SUM
FROM A a
INNER JOIN B b
ON b.ID_C = a.ID
GROUP BY a.ID
ORDER BY a.ID;
Note that the inner join will also remove ID values from the A table which no entries whatsoever in the B table, which seems to be the behavior you want.
you should use inner join and GROUP BY:
SELECT A.ID as ID, SUM(LOG) AS SumLOG
FROM A inner join B ON A.ID = B.ID_C
GROUP BY A.ID
if you needed can use where for ID filter.
I have a three tables
Table 1
Id Department
1 A
2 B
3 C
4 D
Table 2
Id DepartId Name
1 1 ABC
2 1 DEF
3 1 ASD
4 2 FGH
5 2 HJK
6 3 ZXC
Table 3
Id Depart Area
1 A pp
2 B
3 C nn
4 D oo
I need the result
Id Depart Name Area
1 A ABC pp
2 B FGH Null
3 C ZXC nn
4 D NULL oo
I need one matching entry from table 2 and table 3 to corresponding entry in the table 1
Do a left join to also get t1 rows without any reference in the t2 table. GROUP BY to get only 1 row per Department.
select t1.id, t1.Department, min(t2.Name)
from t1
left join t2 on t1.id = t2.DepartId
group by t1.id, t1.Department
I think I would do this with a correlated subquery:
select t1.*,
(select t2.name
from t2
where t1.id = t2.DepartId and rownum = 1
) as t2name
from t1;
This saves the overhead of an aggregation. An index on t2(DepartId, name) is optimal for this query.
by the way not the answer to your specific question but if instead of just one you want all the names you can use listagg
SELECT t1.id,
department,
LISTAGG (name, ',') WITHIN GROUP (ORDER BY name) names
FROM t1, t2
WHERE t1.id = t2.departId(+)
GROUP BY t1.id, department
ORDER BY 1
ID Department Names
1 A ABC,ASD,DEF
2 B FGH, HJK
3 C ZXC
4 D
I have a WHERE clause in a query that needs to see whether the latest entry in a related table meets certain criteria. However, I'm not able to inject the PK of the top query directly into the clause for a number of different reasons.
Is there any way to rewrite the following query to depend on the outer alias (ie. make ALIAS.pk work)? foo has a composite primary key.
(SELECT CASE WHEN EXISTS (
SELECT * FROM (
SELECT n.val1, n.val2 FROM (
SELECT * FROM foo f
WHERE f.val0 = 100 AND f.outerid = ALIAS.pk
ORDER BY f.date DESC
) n
WHERE n.rownum = 1
) t
WHERE t.val1 = 1 AND t.val2 = 2
) THEN 1 ELSE 0 END FROM dual) = 1
Edit: Outer table (bar):
id name city
1 Bob London
2 Mike Atlanta
3 Susan Toronto
Inner table (foo):
outerid date val1 val2 val100 fk1 fk2 fk3
1 2014-11-11 1 2 100 11 523 15
1 2014-11-11 1 2 101 14 12 87
1 2014-11-10 1 2 100 17 1667 12
2 2014-11-11 1 1 100 91 12 188
The primary key for foo is a composite key over fk1..3.
So what I need is to select the latest entry from foo that corresponds to a certain user and check that it has certain characteristics.
Edit 2:
SELECT CASE WHEN ({inner query})=1 THEN 1 ELSE 0 END WHERE id = 1 should return "1" SELECT SELECT CASE WHEN ({inner query})=1 THEN 1 ELSE 0 END WHERE id = 2 should return "0".
This may give you the output you require:
SELECT b.name
FROM bar b
INNER JOIN
(SELECT DISTINCT
f.outerid
FROM
(SELECT f.outerid
, f.val1
, f.val2
, f.date
, max(f.date) OVER
(PARTITION BY f.outerid
ORDER BY f.date) max_date
FROM foo f
WHERE f.val0 = 100) f
WHERE f.date = f.max_date
AND f.val1 = 1
AND f.val2 = 2) f
ON (f.outerid = b.id)
I have this table:
id type otherid
1 4 1234
2 5 1234
3 4 4321
As you can see there are 3 records, 2 of them belongs to otherid "1234" and got type of 4 and 5.
Last record belongs to otherid of "4321" and has only a type of 4.
I need to select all otherid that got only the type 4 and not the type5.
Example: after this select on that table the query shuould return only the record 3
Thanks
add1:
Please consider the TYPE can be any number from 1 up to 20.
I only need otherid that got type 4 but not type 5 ( except than that they can have any other type )
add2:
using mysql 5.1
This is kind of a workaround
SELECT * FROM (
SELECT GROUP_CONCAT('|',type,'|') type,other_id FROM table GROUP BY otherid
) t WHERE type LIKE '%|4|%' AND type NOT LIKE '%|5|%'
You could use a not exists subquery:
select distinct otherid
from YourTable as yt1
where yt1.type = 4
and not exists
(
select *
from YourTable as yt2
where yt1.otherid = yt2.otherid
and yt1.type <> yt2.type -- use this line for any difference
and yt2.type = 5 -- or this line to just exclude 5
)
Another way is by using a left join from where you exclude rows that have both type 4 and 5:
select a.*
from table1 a
left join table1 b on b.otherid = a.otherid and b.type = 5
where a.type = 4 and b.id is null
Sorry for posting this question again. I rephrased my question a little bit.
I am trying to write a query to return rows from Table-A where multiple rows found in Table-B with STATUS = 1 for each CID column from Table-A.
So in this example CID 100 has two records found in Table-B and STATUS = 1. So I want to write a query to return this row from Table-A. I know this is a weird table design. Please help.
Here are the tables with sample data.
Table-A
-----------------------------------------
AID Name CID
---------------------------------------
10 test1 100
12 test1 100
13 test2 101
14 test2 101
15 test3 102
Table-B
------------------------------------
bID AID status
-----------------------------------
1 10 1
2 12 1
3 14 1
4 15 1
Try this query:
SELECT TableA.CID
FROM TableA
JOIN TableB ON TableA.AID = TableB.AID
WHERE TableB.status = 1
GROUP BY TableA.CID
HAVING COUNT(*) > 1
It returns 100 for your example data.
Something like this?
select aid,
status
from (select aid,
count(*) as cnt
from tableA
group by aid) as aggregated
left join tableB on tableB.aid = aggregated.aid
where aggregated.cnt > 1
If your using SQL:
WITH tableBView AS
(
SELECT AID AS xxxAID
FROM [Table-B]
WHERE status = 1
GROUP BY AID
HAVING COUNT(*) > 0
)
SELECT *
FROM [Table-A]
WHERE EXISTS (SELECT * FROM tableBView WHERE xxxAID = AID)
SELECT *
FROM Table-A a
WHERE a.CID IN
(
SELECT a.CID FROM Table-A a JOIN Table-B b USING (AID)
GROUP BY a.CID
WHERE b.status = 1
HAVING count(*) > 1
)
This is a very verbose way to do it.
Selects all columns from Table-A on rows where AID match between Table-A and Table-B and more than one row with the same CID exists in Table-A:
(Btw, I wouldn't use "-" in your table/column names. Use "_" instead.)
select
derived_table.AID,
derived_table.Name,
derived_table.CID
from
(select
table_A.AID,
table_A.Name,
table_A.CID,
count(table_A.CID) c
from
Table_A
inner join Table_B on (Table_A.AID = table_B.AID)
group by table_A.CID
) derived_table
where
c > 1