Selecting greatest value of B for equal entries in A - oracle

Consider the following table:
A | B
-----|------
123 | 1
456 | 2
123 | 5
456 | 0
789 | 3
789 | 9
123 | 6
I want to get the following output:
A | B
-----|------
123 | 6
456 | 2
789 | 9
In other words: the greatest value of B for each equal value in A.
The initial table above comes already from another query which only selects duplicates of A:
select A, B from tbl where A in (
select A from tbl
group by A
having count(A) > 1
);
I tried wrapping/integrating another grouping function with and without max(B) around/into this query, but no success.
How can I get the desired output?

Just use max:
select A, max(B)
from tbl
group by A
having count(A) > 1

maybe I'm being naive here, but:
SELECT tbl2.A, MAX(tbl2.B) FROM
(select A, B from tbl where A in (
select A from tbl
group by A
having count(A) > 1
)) as tbl2
GROUP BY tbl2.A
seems like it should work.

Related

Insert data to table from another table containing null values and replace null values with the original table 1 values

I want to match first column of both table and insert table 2 values to table 1 . But if Table 2 values are null leave table 1 vlaues as it is .I am using Hive to dothis .Please help.
You need to use coalesce to get non null value to populate b column and case statement to make decision to populate c column.
Example:
hive> select t1.a,
coalesce(t2.y,t1.b)b,
case when t2.y is null then t1.c
else t2.z
end as c
from table1 t1 left join table2 t2 on t1.a=t2.x;
+----+-----+----+--+
| a | b | c |
+----+-----+----+--+
| a | xx | 5 |
| b | bb | 2 |
| c | zz | 7 |
| d | dd | 4 |
+----+-----+----+--+

Lag and Lead to next month

TABLE: HIST
CUSTOMER MONTH PLAN
1 1 A
1 2 B
1 2 C
1 3 D
If I query:
select h.*, lead(plan) over (partition by customer order by month) np from HIST h
I get:
CUSTOMER MONTH PLAN np
1 1 A B
1 2 B C
1 2 C D
1 3 D (null)
But I wanted
CUSTOMER MONTH PLAN np
1 1 A B
1 2 B D
1 2 C D
1 3 D (null)
Reason being, next month to 2 is 3, with D. I'm guessing partition by customer order by month doesn't work the way I thought.
Is there a way to achieve this in Oracle 12c?
One way to do it is to use RANGE partitioning with the MIN analytic function. Like this:
select h.*,
min(plan) over
(partition by customer
order by month
range between 1 following and 1 following) np
from HIST h;
+----------+-------+------+----+
| CUSTOMER | MONTH | PLAN | NP |
+----------+-------+------+----+
| 1 | 1 | A | B |
| 1 | 2 | B | D |
| 1 | 2 | C | D |
| 1 | 3 | D | |
+----------+-------+------+----+
When you use RANGE partitioning, you are telling Oracle to make the windows based on the values of the column you are ordering by rather than making the windows based on the rows.
So, e.g.,
ROWS BETWEEN 1 following and 1 following
... will make a window containing the next row.
RANGE BETWEEN 1 following and 1 following
... will make a window containing all the rows having the next value for month.
UPDATE
If it is possible that some values for MONTH might be skipped for a given customer, you can use this variant:
select h.*,
first_value(plan) over
(partition by customer
order by month
range between 1 following and unbounded following) np
from h
+----------+-------+------+----+
| CUSTOMER | MONTH | PLAN | NP |
+----------+-------+------+----+
| 1 | 1 | A | B |
| 1 | 3 | B | D |
| 1 | 3 | C | D |
| 1 | 4 | D | |
+----------+-------+------+----+
You can use LAG/LEAD twice. The first time to check for duplicate months and to set the value to NULL in those months and the second time use IGNORE NULLS to get the next monthly value.
It has the additional benefit that if months are skipped then it will still find the next value.
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE HIST ( CUSTOMER, MONTH, PLAN ) AS
SELECT 1, 1, 'A' FROM DUAL UNION ALL
SELECT 1, 2, 'B' FROM DUAL UNION ALL
SELECT 1, 2, 'C' FROM DUAL UNION ALL
SELECT 1, 3, 'D' FROM DUAL UNION ALL
SELECT 2, 1, 'E' FROM DUAL UNION ALL
SELECT 2, 1, 'F' FROM DUAL UNION ALL
SELECT 2, 3, 'G' FROM DUAL UNION ALL
SELECT 2, 5, 'H' FROM DUAL;
Query 1:
SELECT CUSTOMER,
MONTH,
PLAN,
LEAD( np ) IGNORE NULLS OVER ( PARTITION BY CUSTOMER ORDER BY MONTH, PLAN, ROWNUM ) AS np
FROM (
SELECT h.*,
CASE MONTH
WHEN LAG( MONTH ) OVER ( PARTITION BY CUSTOMER ORDER BY MONTH, PLAN, ROWNUM )
THEN NULL
ELSE PLAN
END AS np
FROM hist h
)
Results:
| CUSTOMER | MONTH | PLAN | NP |
|----------|-------|------|--------|
| 1 | 1 | A | B |
| 1 | 2 | B | D |
| 1 | 2 | C | D |
| 1 | 3 | D | (null) |
| 2 | 1 | E | G |
| 2 | 1 | F | G |
| 2 | 3 | G | H |
| 2 | 5 | H | (null) |
Just so that it is listed here as an option for Oracle 12c (onward), you can use an apply operator for this style of problem
select
h.customer, h.month, h.plan, oa.np
from hist h
outer apply (
select
h2.plan as np
from hist h2
where h.customer = h.customer
and h2.month > h.month
order by month
fetch first 1 rows only
) oa
order by
h.customer, h.month, h.plan
I don't know of any Oracle 12c public fiddles so, an example in SQL Server can be found here: http://sqlfiddle.com/#!18/cd95e/1
| customer | month | plan | np |
|----------|-------|------|--------|
| 1 | 1 | A | C |
| 1 | 2 | B | D |
| 1 | 2 | C | D |
| 1 | 3 | D | (null) |

oracle - querying NULL values in unpivot query

I want to fetch records from oracle DB where column value is NULL. Also i am using unpivot in my query. Somehow NULL values are not getting selected because of unpivot keyword. Can you please help me about how to get rows for the same when using unpivot.
EDIT:
SELECT a.emp_id, a.emp_dept, b.emp_location
FROM employee a,
location b UNPIVOT (emp_id
FOR dummy_id
IN (emp_id AS 'EMP_ID', last_date AS 'LAST_DATE'))
WHERE emp_id = 123 AND b.emp_loc_id = 'india' AND b.location IS NULL;
Use UNPIVOT INCLUDE NULLS:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE test ( id, a, b, c, d ) AS
SELECT 1, 1, 2, 3, 4 FROM DUAL UNION ALL
SELECT 2, 1, NULL, 3, NULL FROM DUAL;
Query 1:
SELECT *
FROM test
UNPIVOT INCLUDE NULLS ( value FOR name IN ( a, b, c, d ) )
Results:
| ID | NAME | VALUE |
|----|------|--------|
| 1 | A | 1 |
| 1 | B | 2 |
| 1 | C | 3 |
| 1 | D | 4 |
| 2 | A | 1 |
| 2 | B | (null) |
| 2 | C | 3 |
| 2 | D | (null) |

Remove duplicate values from a listagg in oracle

I have used listagg to concat and list books along with the supplementary books name.
SELECT DISTINCT SUBSTR(LISTAGG(',-'||B1.BOOK_NO||','||B1.BOOK_NAME||','||A.AUTHOR_NAME||',-'||B2.BOOK_NO||','||B2.BOOK_NAME) WITHIN GROUP (ORDER BY B2.BOOK_NO),2)
FROM BOOK_LIST B1
INNER JOIN AUTHORS A ON A.AUTHOR_NO=B1.AUTHOR_NO
INNER JOIN SUPPLEMENTARY B2 ON B2.BOOK_NO = B1.BOOK_SUP_NO
WHERE B1.SEQ = 123;
But since the number of supplementary books are more i get the main book name repeatedly.
Is there a way to remove the duplicate main book name and number.
My ouput is like this
-99,Anders Carlson ,-109,John Stuart,-99,Anders Carlson ,-47,James Anderson
Here the value 99 is repeated i want only one 99.
Desired Output:
-99,Anders Carlson ,-109,John Stuart,-47,James Anderson
DB data:
Book_list:
NO | MAIN_BOOK_NO | MAIN_BOOK_NAME | BOOK_SUP_NO | AUTHOR_NO
1 | 12 | xyz | 5 | 2
2 | 22 | abc | 7 | 4
Authors:
NO | AUTHOR_NO | AUTHOR_NAME
1 | 2 | Alex
2 | 3 | Leonard
3 | 4 | Benjamin
Supplementary:
NO | BOOK_NO | BOOK_NAME
1 | 5 | ABC
2 | 5 | XYZ
3 | 7 | LMN
4 | 7 | DEF
5 | 7 | NEW
The output should be like
NAME
12,xyz,Alex,-5,ABC,-5,XYZ
22,abc,Benjamin,-7,LMN,-7,DEF,-7,NEW
Similarly for the entire data in the table
If I understand you correctly, you need to append the list of supplementary books to the main book, so you're actually after something like:
SELECT B1.MAIN_BOOK_NO||','||B1.MAIN_BOOK_NAME||',-'||
LISTAGG(B2.BOOK_NO||','||B2.BOOK_NAME, ',-') WITHIN GROUP (ORDER BY B2.BOOK_NO)
FROM BOOK_LIST B1
INNER JOIN AUTHORS A ON A.AUTHOR_NO=B1.AUTHOR_NO
INNER JOIN SUPPLEMENTARY B2 ON B2.BOOK_NO = B1.BOOK_SUP_NO
WHERE B1.SEQ = 123
GROUP BY B1.MAIN_BOOK_NO, B1.MAIN_BOOK_NAME;
See if this works
select T1.MAIN_BOOK_NO, T11.MAIN_BOOK_NAME, LISTAGG(',-'||',-'||T1.BOOK_NO||','||T1.BOOK_NAME) WITHIN GROUP (order by T1.BOOK_NO)
from
(
SELECT B1.MAIN_BOOK_NO, B1.MAIN_BOOK_NAME, B2.BOOK_NO, B2.BOOK_NAME
FROM BOOK_LIST B1
INNER JOIN AUTHORS A ON A.AUTHOR_NO=B1.AUTHOR_NO
INNER JOIN SUPPLEMENTARY B2 ON B2.BOOK_NO = B1.BOOK_SUP_NO
WHERE B1.SEQ = 123
group by B1.MAIN_BOOK_NO, B1.MAIN_BOOK_NAME, B2.BOOK_NO, B2.BOOK_NAME
order by B2.BOOK_NO
) T1
group by T1.MAIN_BOOK_NO, T1.MAIN_BOOK_NAME;

LINQ retrieve values from a table that of which fields(of a certain column) are not equal of another table

I have two tables which have a column in common, how can I retrieve the first table where the values of it's column are not equal to the values of the column of the other table?
Here is an example:
table1ID | foo | fooBool table2ID | foo | fooin
---------------------------- -----------------------------
1 | test1 | true 1 | test2 | 5
2 | test2 | true 2 | test3 | 7
3 | test3 | true
4 | test4 | false
Therefore the result of the LINQ query for the first table that has values of foo not equal that of table2 are:
table1ID | foo | fooBool
---------------------------
1 | test1 | true
4 | test4 | false
var results = from t1 in db.table1
from t2 in db.table2
where t1.foo != t2.foo
select t1
You could also use the Intersect() IEnumerable Extension
var results = db.table1.Intersect(db.table2);
Or in LINQ
var codes = from intersected in db.table1.Intersect(db.table2)
select intersected.foo;
This would produce
results
----------
test2
test3
Update
Thanks to Joe for pointing out that Intersect would produce the common items (added sample above). What you would want is the Except extension method.
var results = db.table1.Except(db.table2);
Or in LINQ
var codes = from diff in db.table1.Except(db.table2)
select diff.foo;
This would produce
results
----------
test1
test4

Resources