linking the query in the select statement - sql

From the query i am able to return the output as expected ..
Is it possible to write the same query in the below marked quotes using select statement:
SELECT ACC.ACCOUNT_NUM AS ACCOUNT_NUMBER,
ACC.ACCOUNT_NAME AS ACCOUNT_NAME,
ADR.ADDRESS_1 AS BUILDING_TYPE,
ACC.CUSTOMER_REF AS CUSTOMER_ID,
CAT.BC AS BILL_CYCLE,
CES.EVENT_SOURCE AS TELEPHONE_NUMBER,
AAT.PACKAGE_NAME AS PROMO_PACKAGE,
PRD.PRODUCT_NAME AS SERVICES,
(SELECT COUNT (DISTINCT (EVENT_SOURCE))
FROM CUSTEVENTSOURCE CES1
WHERE CES1.CUSTOMER_REF = ACC.CUSTOMER_REF AND END_DTM IS NULL)
AS TOTAL_NUMBER,
("SELECT LISTAGG (EVENT_SOURCE, ', ')
WITHIN GROUP (ORDER BY EVENT_SOURCE)
FROM CUSTEVENTSOURCE
WHERE CUSTOMER_REF = ACC.CUSTOMER_REF AND END_DTM IS NULL")
AS ALL_IPHONE_NUMBERS,
CUS.COMPANY_NAME AS COMPANY_NAME,
ADR.ADDRESS_1
|| ' '
|| ADR.ADDRESS_2
|| ' '
|| ADR.ADDRESS_3
|| ' '
|| ADR.ADDRESS_4
|| ' '
|| ADR.ADDRESS_5
AS BILLING_ADDRESS,
(SELECT ADR.ADDRESS_1
FROM ADDRESS ADR
WHERE ADR.CUSTOMER_REF = ACC.CUSTOMER_REF
AND ADDRESS_SEQ IN
(SELECT ADDRESS_SEQ
FROM CUSTPRODUCTADDRESS CPA
WHERE CPA.CUSTOMER_REF = ADR.CUSTOMER_REF
AND PRODUCT_SEQ IN
(SELECT PRODUCT_SEQ
FROM CUSTPRODUCTSTATUS CPS
WHERE CPS.CUSTOMER_REF =
CPA.CUSTOMER_REF
AND EFFECTIVE_DTM =
(SELECT MAX (
EFFECTIVE_DTM)
FROM CUSTPRODUCTSTATUS CPS1
WHERE CPS1.CUSTOMER_REF =
CPS.
CUSTOMER_REF))))
AS INSTALLATION_ADDRESS,
(SELECT ACS.EFFECTIVE_DTM
FROM ACCOUNTSTATUS ACS
WHERE ACS.ACCOUNT_NUM = ACC.ACCOUNT_NUM
AND ACS.EFFECTIVE_DTM =
(SELECT MAX (EFFECTIVE_DTM)
FROM ACCOUNTSTATUS ACS1
WHERE ACS1.ACCOUNT_NUM = ACS.ACCOUNT_NUM
AND ACCOUNT_STATUS = 'OK'))
AS ACTIVATION_DATE,
(SELECT ACS.ACCOUNT_STATUS
FROM ACCOUNTSTATUS ACS
WHERE ACS.ACCOUNT_NUM = ACC.ACCOUNT_NUM
AND ACS.EFFECTIVE_DTM =
(SELECT MAX (EFFECTIVE_DTM)
FROM ACCOUNTSTATUS ACS1
WHERE ACS1.ACCOUNT_NUM = ACS.ACCOUNT_NUM))
AS ACCOUNT_STATUS,
(SELECT ACS.STATUS_REASON_TXT
FROM ACCOUNTSTATUS ACS
can u please help me out..I am new to sql..please
Thanks in advance..
The above needs to be changed within the "" double quotes
The output looks like:
ACCOUNT_NUMBER ACCOUNT_NAME BUILDING_TYPE CUSTOMER_ID TELEPHONE_NUMBER PROMO_PACKAGE TOTAL_NUMBER ALL_IPHONE_NUMBERS,
MTX000110 John xxxx 10002 123456 yyy 3 001-003,004,007
If the account has 3 totoal_number then in the All_iphone_numbers column if the values have sequenced numbers then it should '-' with separator otherwise it has to display the number
If I use the " " doubl quoted array..I am getting the output as comma separated values whic is incorrec result..the result should looks like "if the values have sequenced numbers then it should '-' with separator otherwise it has to display the number".

Try this: (Note: I dont have 11G R2, so I used wm_concat to test it, which is not supported, but if you have 11G R2, then use LISTAGG)
WITH TABIBITOSAN
AS (SELECT
ACCOUNT_REF,
"number",
TO_NUMBER ( "number" )
- ROW_NUMBER ( ) OVER (ORDER BY TO_NUMBER ( "number" ))
AS GRP
FROM
TBL),
DATASET1
AS (SELECT
ACCOUNT_REF,
CASE
WHEN MIN ( "number" ) = MAX ( "number" )
THEN
MIN ( "number" )
ELSE
MIN ( "number" )
|| '-'
|| MAX ( "number" )
END
AS RANGES
FROM
TABIBITOSAN
GROUP BY
ACCOUNT_REF,
GRP
ORDER BY
ACCOUNT_REF,
MIN ( "number" ))
SELECT
ACCOUNT_REF,
WM_CONCAT ( RANGES ) AS NEW_RANGES
FROM
DATASET1
GROUP BY
ACCOUNT_REF;
Results:
101 002-004,006-008,011
Using LISTAGG
SELECT
ACCOUNT_REF,
LISTAGG ( RANGES,
',' )
WITHIN GROUP (ORDER BY RANGES)
AS NEW_RANGES
FROM
DATASET1
WHERE
ACCOUNT_REF = ACC.ACCOUNT_REF
GROUP BY
ACCOUNT_REF

Related

Is it possible to replace values in row to values from the same table by the reference in Oracle SQL

There is a table that contains values that are used in formulas. There are simple variables, that do not contain any expression, and also there are some variables that combined from simple variables into formula. I need to figure out if is it possible to do a SELECT query to get a readable formula based on aliases it contains. Each of these aliases could be used in other formulas.
Let's say that there are two tables:
ITEM TABLE
ID
Name
FORMULA_ID
1
Item name 1
f_3
2
Item name 2
f_26
FORMULA TABLE
ID
EXPRESSION
ALIASE
NAME
f_1
null
var_100
Ticket
f_2
null
var_200
Amount
f_3
var_100 * var_200
var_300
Some description
So is there any chance to query, with result like:
ITEM_NAME
READABLE_EXPRESSION
Item name 1
Ticket * Amount
Try this:
with items(ID,Name,Formula_Id) AS (
select 1, 'Item name 1', 'f_3' from dual union all
select 2, 'Item name 2', 'f_26' from dual
),
formulas (ID, EXPRESSION, ALIAS, NAME) as (
select 'f_1', null, 'var_100', 'Ticket' from dual union all
select 'f_2', null, 'var_200', 'Amount' from dual union all
select 'f_3', 'var_100 * var_200', 'var_300', 'Some description' from dual
),
rnformulas (id, EXPRESSION, ALIAS, NAME, rn) as (
select fm.*, row_number() over(order by id) as rn from formulas fm
),
recsubstitute( lvl, item_id, rn, expression ) as (
select 1, it.id, 0, fm.expression
from items it
join rnformulas fm on it.formula_id = fm.id
union all
select lvl+1, item_id, fm.rn, replace(r.expression, fm.alias, fm.name)
from recsubstitute r
join rnformulas fm on instr(r.expression, fm.alias) > 0 and fm.rn > r.rn
)
select item_id, expression from (
select item_id, expression, row_number() over(partition by item_id order by lvl desc, rn asc) as rn
from recsubstitute
)
where rn = 1
;
ITEM_ID EXPRESSION
---------- ------------------------------------------------------------
1 Ticket * Amount
Note that it's far to be bullet proof against all situations, especially recursion in the aliases.
Some improvement with another set of data:
with items(ID,Name,Formula_Id) AS (
select 1, 'Item name 1', 'f_3' from dual union all
select 2, 'Item name 2', 'f_4' from dual
),
formulas (ID, EXPRESSION, ALIAS, NAME) as (
select 'f_1', null, 'var_100', 'Ticket' from dual union all
select 'f_2', null, 'var_200', 'Amount' from dual union all
select 'f_3', 'var_100 * var_200', 'var_300', 'Some description' from dual union all
select 'f_4', 'var_300', null, 'Other description' from dual
),
rnformulas (id, EXPRESSION, ALIAS, NAME, rn) as (
select fm.*, row_number() over(order by id) as rn from formulas fm
),
recsubstitute( lvl, item_id, rn, expression ) as (
select 1, it.id, 0, fm.expression
from items it
join rnformulas fm on it.formula_id = fm.id
union all
select lvl+1, item_id, fm.rn, replace(r.expression, fm.alias, nvl(fm.expression,fm.name))
from recsubstitute r
join rnformulas fm on instr(r.expression, fm.alias) > 0
)
select item_id, expression from (
select item_id, expression, row_number() over(partition by item_id order by lvl desc, rn asc) as rn
from recsubstitute
)
where rn = 1
;
1 Ticket * Amount
2 Ticket * Amount
Split the space-delimited formulas into rows. Join the expression parts to the aliases and replace the alias with the name. Join this to the item_table using LISTAGG to concatenate the rows back into a single column.
WITH formula_split AS (
SELECT DISTINCT ft.id
,level lvl
,regexp_substr(ft.expression,'[^ ]+',1,level) expression_part
FROM formula_table ft
CONNECT BY ( ft.id = ft.id
AND level <= length(ft.expression) - length(replace(ft.expression,' ')) + 1 ) START WITH ft.expression IS NOT NULL
),readable_tbl AS (
SELECT ft.id
,ft.lvl
,replace(ft.expression_part,ftn1.aliase,ftn1.name) readable_expression
FROM formula_split ft
LEFT JOIN formula_table ftn1 ON ( ft.expression_part = ftn1.aliase )
)
SELECT it.name item_name
,LISTAGG(readable_expression,' ') WITHIN GROUP(ORDER BY lvl) readable_expression
FROM item_table it
JOIN readable_tbl rt ON ( it.formula_id = rt.id )
GROUP BY it.name
With sample data create CTE (calc_data) for modeling
WITH
items (ITEM_ID, ITEM_NAME, FORMULA_ID) AS
(
Select 1, 'Item name 1', 'f_3' From Dual Union All
Select 2, 'Item name 2', 'f_26' From Dual
),
formulas (FORMULA_ID, EXPRESSION, ALIAS, ELEMENT_NAME) AS
(
Select 'f_1', null, 'var_100', 'Ticket' From Dual Union All
Select 'f_2', null, 'var_200', 'Amount' From Dual Union All
Select 'f_3', 'var_100 * var_200', 'var_300', 'Some description' From Dual
),
calc_data AS
( SELECT e.ITEM_NAME, e.FORMULA_ID, e.FORMULA, e.X, e.OPERAND, e.Y,
ROW_NUMBER() OVER(Partition By e.ITEM_NAME Order By e.FORMULA_ID) "RN", f.ELEMENT_NAME
FROM( Select CAST('.' as VARCHAR2(32)) "FORMULA", i.ITEM_NAME, f.FORMULA_ID,
SubStr(Replace(f.EXPRESSION, ' ', ''), 1, InStr(Replace(f.EXPRESSION, ' ', ''), '*') - 1) "X",
CASE
WHEN InStr(f.EXPRESSION, '+') > 0 THEN '+'
WHEN InStr(f.EXPRESSION, '-') > 0 THEN '-'
WHEN InStr(f.EXPRESSION, '*') > 0 THEN '*'
WHEN InStr(f.EXPRESSION, '/') > 0 THEN '/'
END "OPERAND",
--
SubStr(Replace(f.EXPRESSION, ' ', ''), InStr(Replace(f.EXPRESSION, ' ', ''), '*') + 1) "Y"
From formulas f
Inner Join items i ON(f.FORMULA_ID = i.FORMULA_ID)
) e
Inner Join formulas f ON(f.FORMULA_ID <> e.FORMULA_ID)
)
Main SQL with MODEL clause
SELECT ITEM_NAME, FORMULA
FROM ( SELECT *
FROM calc_data
MODEL
PARTITION BY (ITEM_NAME)
DIMENSION BY (RN)
MEASURES (X, OPERAND, Y, FORMULA, ELEMENT_NAME)
RULES ( FORMULA[1] = ELEMENT_NAME[1] || ' ' || OPERAND[1] || ' ' || ELEMENT_NAME[2] )
)
WHERE RN = 1
R e s u l t :
ITEM_NAME
FORMULA
Item name 1
Amount * Ticket
Just as an option...
The same result without any analytic functions, pseudo columns, unions, etc... - just selecting over and over and over. Not readable, though...
Select
i.ITEM_NAME,
REPLACE( REPLACE( (Select EXPRESSION From formulas Where FORMULA_ID = f.FORMULA_ID),
(Select Min(ALIAS) From formulas Where FORMULA_ID <> f.FORMULA_ID),
(Select ELEMENT_NAME From formulas Where FORMULA_ID <> f.FORMULA_ID And ALIAS = (Select Min(ALIAS) From formulas Where FORMULA_ID <> f.FORMULA_ID) )
) ||
REPLACE( (Select EXPRESSION From formulas Where FORMULA_ID = f.FORMULA_ID),
(Select Max(ALIAS) From formulas Where FORMULA_ID <> f.FORMULA_ID),
(Select ELEMENT_NAME From formulas Where FORMULA_ID <> f.FORMULA_ID And ALIAS = (Select Max(ALIAS) From formulas Where FORMULA_ID <> f.FORMULA_ID) )
),
(SELECT Max(ALIAS) From formulas Where FORMULA_ID <> f.FORMULA_ID ) || (Select Min(ALIAS) From formulas Where FORMULA_ID <> f.FORMULA_ID) ||
SubStr(f.EXPRESSION, InStr(f.EXPRESSION, ' ', 1, 1), (InStr(f.EXPRESSION, ' ', 1, 2) - InStr(f.EXPRESSION, ' ', 1, 1)) + 1 ), ''
) "FORMULA"
From
formulas f
Left Join
items i ON(i.FORMULA_ID = f.FORMULA_ID)
Where i.ITEM_NAME Is Not Null
Thank you all for your answers!
I've decided to create a pl/sql function, just to modify a formula to readable row. So the function just looks for variables using regex, and uses indexes to replace every variable with a name.
CREATE OR REPLACE FUNCTION READABLE_EXPRESSION(inExpression IN VARCHAR2)
RETURN VARCHAR2
IS
matchesCount INTEGER;
toReplace VARCHAR2(32767);
readableExpression VARCHAR2(32767);
selectString VARCHAR2(32767);
BEGIN
matchesCount := REGEXP_COUNT(inExpression, '(var_)(.*?)');
IF matchesCount IS NOT NULL AND matchesCount > 0 THEN
readableExpression := inExpression;
FOR i in 1..matchesCount
LOOP
toReplace := substr(inExpression, REGEXP_INSTR(inExpression, '(var_)(.*?)', 1, i, 0),
REGEXP_INSTR(inExpression, '(var_)(.*?)', 1, i, 1) -
REGEXP_INSTR(inExpression, '(var_)(.*?)', 1, i, 0)
);
SELECT DISTINCT F.NAME
INTO selectString
FROM FORMULA F
WHERE F.ALIASE = toReplace FETCH FIRST 1 ROW ONLY;
readableExpression := REPLACE(readableExpression,
toReplace,
selectString
);
end loop;
end if;
return readableExpression;
END;
So such function returns 1 result row with replaced values for 1 input row with FORMULA. All you need to do is join the ITEM and FORMULA tables in the SELECT.
SELECT item.name, READABLE_EXPRESSION(formula.expression)
FROM item
JOIN formula ON item.formula_id = formula.id;
Please note that the tables are fictitious so as not to reveal the actual data structure, so there might be some inaccuracies. But the general idea should be clear.

Show Zero if there is no record count - ORACLE SQL query

The below Oracle query gives if there are any different errors with error_message and Serial_num.
If there is ZERO or No Different error count instead of showing Blank/Null result. How can i see the output like this? I tried with NVL(error_message,0) and COALESCE (Sum(total),0) but not getting the desired output.
Expected output:
1 Different Errors: 0
Oracle SQL Query:
SELECT
1 as Index_Num,
CONCAT('Different Errors: ', error_message || '# ' || serial_num),
SUM(total)
FROM (
SELECT error_message, serial_num, COUNT(*) total
FROM Table1
WHERE error_message NOT LIKE '%INVALID%'
GROUP BY error_message, serial_num
)
GROUP BY error_message, serial_num
Create a CTE for the subquery and use UNION ALL with NOT EXISTS to cover the case that the CTE does not return any rows:
WITH cte AS (
SELECT error_message, serial_num, COUNT(*) total
FROM Table1
WHERE error_message NOT LIKE '%INVALID%'
GROUP BY error_message, serial_num
)
SELECT
1 as Index_Num,
CONCAT(
'Different Errors: ',
list_agg(error_message || '# ' || serial_num) within group (order by error_message)
),
SUM(total)
FROM cte
UNION ALL
SELECT 1, 'Different Errors: ', 0
FROM dual
WHERE NOT EXISTS (SELECT 1 FROM cte)
D'oh! Looks like I took too long. Here's another option for posterity:
SELECT
1,
CONCAT(
'Different Errors: ',
CASE
WHEN src.error_message IS NULL THEN ''
ELSE src.error_message || ' # ' || src.serial_num
END
) Summary,
COALESCE(src.total, 0) AS total
FROM dual -- Get a seed row (in case there are no rows in error table)
LEFT JOIN (
SELECT error_message, serial_num, COUNT(*) total
FROM Table1
WHERE error_message NOT LIKE '%INVALID%'
GROUP BY error_message, serial_num
) src ON 0=0
SQL Fiddle
It is not exactly what you are asking for, but might prove useful. You can easily add a row with the total number of errors, using grouping sets:
SELECT 1 as Index_Num,
('Different Errors: ' || error_message || '# ' || serial_num),
COUNT(*) as total
FROM Table1
WHERE error_message NOT LIKE '%INVALID%'
GROUP BY GROUPING SETS ( (error_message, serial_num), () );
Alas, this produces the summary row even when there are errors. It occurs to me that you might find this useful.

SQL query group by and select the maximum absolute value

"Table1" structure is as shown below:
source table table1
Player_NAME || Player_NUMBER || Client_name || Client_country || Player_country|| Rating
GERALD || A1234 || BENFIELD || IND || IND || 76
GERALD ||A6578 || ROTFIELD || USA || USA || 64
KUMAR || P1234 || LFV || ARG || ARG || -24
KUMAR || P5678 ||JEURASIN || ARG || TUR ||-32
KUMAR || P0101 ||ARGENIA ||ARG ||POL ||-16
ANDREW ||R1234 || GENMAD || GER || GER || 23
I need to select the records from above table “Table1” and copy them to “Table2”.
I need to select the player record from table1 which satisfy the below conditions :
If a player has multiple client_names or multiple client_country, then select the record which has the maximum value of rating . If it is negavie, then take the absolute value of that value. i.e if the rating is -10 and -34, then take the absolute value which is greatest. i. e by taking absolute value it is 10,34 and 34 is greatest one.
For ex: Kumar has 3 diff client names or 3 diff client_country ,so for kumar the record with rating 32 should be selected ,after taking the absolute value of it.
Below is the expected output:
Player_NAME || Player_NUMBER ||Client_name || Client_country ||Player_country|| Rating
GERALD || A1234 || BENFIELD|| IND|| IND|| 76
KUMAR || P5678 || JEURASIN ||ARG ||TUR || -32
ANDREW || R1234 || GENMAD ||GER ||GER || 23
destination table-'table2'
You can try something like this:
INSERT INTO Table2
(
Player_Name,
Player_Number,
Cliet_Name,
Client_country,
Player_country,
Rating
)
SELECT
Player_Name,
Player_Number,
Cliet_Name,
Client_country,
Player_country,
MAX(ABS(Rating)) OVER (PARTITION BY player_Name ORDER BY Cliet_Name,
Client_country) as Rating
FROM
table1
If your DBMS supports Analytical Function you can utilize ROW_NUMBER:
select ... -- all columns but rn
from
(
select ... -- all columns
,row_number()
over (partition by player_name
order by abs(Rating) desc as rn
from table1
) as dt
where rn = 1;
Otherwise use a Correlated Subquery:
select *
from table1 as t1
where abs(rating) =
( select max(abs(rating))
from table1 as t2
where t1.player_name = t2.player_name
)
If you got multiple rows with the same max(abs(rating)) #1. will select one of them randomly, but #2 will select all.
I guess, this query will work:
select
max(abs(Rating))
from Table1
group by Player_NAME
To insert data into Table2, you can do it like so:
INSERT INTO Table2 (
Player_Name,
Player_Number,
Cliet_Name,
Client_country,
Player_country,
Rating
)
SELECT
t1.Player_Name,
t1.Player_Number,
t1.Cliet_Name,
t1.Client_country,
t1.Player_country,
t1.Rating
FROM Table1 t1
INNER JOIN (
SELECT
Player_NAME,
MAX(ABS(Rating)) as Rating
FROM Table1
GROUP BY Player_NAME
) t2 ON t2.Player_NAME = t1.Player_NAME AND ABS(t1.Rating) = t2.Rating

Listagg Overflow function implementation (Oracle SQL)

I am using LISTAGG function for my query, however, it returned an ORA-01489: result of string concatenation is too long error. So I googled that error and found out I can use ON OVERFLOW TRUNCATE and I implemented that into my SQL but now it generates missing right parenthesis error and I can't seem to figure out why?
My query
SELECT DISTINCT cust_id, acct_no, state, language_indicator, billing_system, market_code,
EMAIL_ADDR, DATE_OF_CHANGE, TO_CHAR(DATE_LOADED, 'DD-MM-YYYY') DATE_LOADED,
(SELECT LISTAGG( SUBSTR(mtn, 7, 4),'<br>' ON OVERFLOW TRUNCATE '***' )
WITHIN GROUP (ORDER BY cust_id || acct_no) mtnlist
FROM process.feature WHERE date_loaded BETWEEN TO_DATE('02-08-2018','MM-dd-yyyy')
AND TO_DATE('02-09-2018', 'MM-dd-yyyy') AND cust_id = ffsr.cust_id
AND acct_no = ffsr.acct_no AND filename = 'FEATURE.VB2B.201802090040'
GROUP BY cust_id||acct_no) mtnlist
FROM process.feature ffsr WHERE date_loaded BETWEEN TO_DATE('02-08-2018','MM-dd-yyyy')
AND TO_DATE('02-09-2018','MM-dd-yyyy') AND cust_id BETWEEN 0542185146 AND 0942025571
AND src_ind = 'B' AND filename = 'FEATURE.VB2B.201802090040'
AND letter_type = 'FA' ORDER BY cust_id;
With a little bit of help by XML, you might get it work. Example is based on HR schema.
SQL> select
2 listagg(s.department_name, ',') within group (order by null) result
3 from departments s, departments d;
from departments s, departments d
*
ERROR at line 3:
ORA-01489: result of string concatenation is too long
SQL>
SQL> select
2 rtrim(xmlagg(xmlelement (e, s.department_name || ',')).extract
3 ('//text()').getclobval(), ',') result
4 from departments s, departments d;
RESULT
--------------------------------------------------------------------------------
Administration,Administration,Administration,Administration,Administration,Admin
SQL>
This demo sourced from livesql.oracle.com
-- Create table with 93 strings of different lengths, plus one NULL string. Notice the only ASCII character not used is '!', so I will use it as a delimiter in LISTAGG.
create table strings as
with letters as (
select level num,
chr(ascii('!')+level) let
from dual
connect by level <= 126 - ascii('!')
union all
select 1, null from dual
)
select rpad(let,num,let) str from letters;
-- Note the use of LENGTHB to get the length in bytes, not characters.
select str,
sum(lengthb(str)+1) over(order by str rows unbounded preceding) - 1 cumul_lengthb,
sum(lengthb(str)+1) over() - 1 total_lengthb,
count(*) over() num_values
from strings
where str is not null;
-- This statement implements the ON OVERFLOW TRUNCATE WITH COUNT option of LISTAGG in 12.2. If there is no overflow, the result is the same as a normal LISTAGG.
select listagg(str, '!') within group(order by str) ||
case when max(total_lengthb) > 4000 then
'! ... (' || (max(num_values) - count(*)) || ')'
end str_list
from (
select str,
sum(lengthb(str)+1) over(order by str) - 1 cumul_lengthb,
sum(lengthb(str)+1) over() - 1 total_lengthb,
count(*) over() num_values
from strings
where str is not null
)
where total_lengthb <= 4000
or cumul_lengthb <= 4000 - length('! ... (' || num_values || ')');

Oracle-sql : Query with hiererchical group by

I have a table like:
ID || Ent NAME || FUNCTION
1 || Dupuis || Signatory
1 || Daturt || Decision Maker
1 || Nobel || (Null )
2 || Karl || Decision Maker
2 || Titi || (Null )
3 || Cloves || (Null )
3 || Cardigan || (Null )
I want to get the most "important" people in a pre -established hierarchy (Signatory > Decision Maker > (Null ) )
So the expected result is:
ID Ent || NAME || FUNCTION
1 || Dupuis || Signatory
2 || Karl || Decision Maker
3 || Cardigan || (Null )
for the 3rd , i don't care of person selected .
I work in Oracle with extremely limited right , I can do that SELECT ( and this it is s*** ).
I have a solution bypass but it is extremely ugly and I am not satisfied:
(SELECT "ID Ent" max (NOM), max (FUNCTION)
FROM table
WHERE FUNCTION = 'Signatory' GROUP BY "ID Ent")
UNION
(SELECT "ID Ent" max (NOM), max (FUNCTION)
FROM table
WHERE FUNCTION = 'Decision Maker'
AND "ID Ent" not in (SELECT "ID Ent" FROM table WHERE FUNCTION = 'Signatory')
GROUP BY "ID Ent")
UNION
(SELECT "ID Ent" max (NOM), max (FUNCTION)
FROM table
WHERE FUNCTION = 'Decision Maker'
AND "ID Ent" not in (SELECT "ID Ent" in FUNCTION FROM table WHERE ('Signatory', 'Decision Maker'))
GROUP BY "ID Ent");
Do you have a better way to do this?
I would approach this using analytic functions:
select t.Id, t.Name, t.Function
from (select t.*,
row_number() over (partition by id
order by (case when function = 'Signatory' then 1
when function = 'Decision Maker' then 2
else 3
end)
) as seqnum
from table t
) t
where seqnum = 1;