Oracle-sql : Query with hiererchical group by - sql

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;

Related

Splitting up Group By for similar values in a column within SQL Server

I am trying to split up the values in column5 so that when using a GROUP BY on column5 (seen below) the 2 value isn't all grouped together. Instead, the values will be separated out so that the first value of 2 is in it's own group the the second group is the value 46675 and the final third groupings will be the last couple 2 values. In short, I am looking for a way to split up the 2 value so that it does not aggregate all the 2 values together, but instead splits them into separate groupings (mirroring the 'Groups' column). The intended outcome is to have the each separated 2 values be aggregated together in their own respective groups. Added tidy table and picture from Excel file.
||Column1||Column2||Column3||Column4||Column5||Groups||
|| 1 || NO || A || F || 2 || 1 ||
|| 2 || Yes || B || C || 46 || 2 ||
|| 3 || NO || C || F || 2 || 3 ||
|| 4 || NO || D || F || 2 || 3 ||
Image of Table from Excel File
This is the prototype gaps and islands problem. You're looking for runs in Column5 ordered by Column1:
with data as (
select *,
row_number() over (order by Column1) -
row_number() over (partition by Column5 order by Column1) as grp
)
select * from data order by Column1;

SQL Sylob return a boolean

I am currently discovering the Sylob 5 ERP and I would like some help on one of my queries because the Sylob support is overwhelmed with demands.
I would like my query to display if one of the operations in my fabrication order is late.
I already have a query that displays every operations and if it's late or not:
SELECT
distinct
ordreFabrication.codeOF as Code_OF,
operationOrdreFabrication.libelle as Libelle,
operationOrdreFabrication.centreCharge.libelle || ' (' ||
operationOrdreFabrication.centreCharge.code || ')' as Centre_charge,
operationOrdreFabrication.dateDebutPrevue as Début_prévu,
(
CASE
WHEN current_date() > operationOrdreFabrication.dateDebutPrevue and
operationOrdreFabrication.etatAvancementOperationOF = '0' THEN ('<div style=''color:red''>' ||
'Retard sur le début' || ' </div>')
WHEN current_date() > operationOrdreFabrication.dateFinPrevue and
operationOrdreFabrication.etatAvancementOperationOF != '2' THEN ('<div style=''color:red''>' ||
'Retard sur la fin' || ' </div>')
ELSE ('Aucun retard')
END
) as Retard,
operationOrdreFabrication.dateDebutReelle as Début_Réel,
operationOrdreFabrication.dateFinPrevue as Fin_prévue
FROM
OperationOrdreFabricationEntite as operationOrdreFabrication
left outer join operationOrdreFabrication.ordreFabrication as ordreFabrication
WHERE
operationOrdreFabrication.id not like 'DefaultRecord_%'
AND
operationOrdreFabrication.dateFinValidite is null
AND
ordreFabrication.dateFinValidite is null
AND
operationOrdreFabrication.sousTraitance in ('false')
AND
((current_date() > operationOrdreFabrication.dateDebutPrevue and
operationOrdreFabrication.etatAvancementOperationOF = '0'
) OR (current_date() > operationOrdreFabrication.dateFinPrevue and
operationOrdreFabrication.etatAvancementOperationOF != '2'))
ORDER BY
1 asc
But I want this to return true or false when my case returns anything else than "Aucun retard"
so I can use it as a subquery
Looks like you want EXISTS (NOT EXISTS)
select exists(
select 1
from (
--you original query
) t
where retard = 'Aucun retard') flag

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

Convert row data to new columns based on common ID value

I have an MS Access database for product ID list for each client ID.
Currently, the table is set up as the following:
CLI_ID || PRODUCT_ID
963506 || 49001608
968286 || 49001645
987218 || 00048038
987218 || 49001401
999999 || 9999999
999999 || 9999998
999999 || 9999997
999999 || 9999996
I would like to transpose the data to look like this:
CLI_ID || PRODUC1 || PRODUC2 || PRODUC3 || PRODUC4 ||
963506 || 49001608 ||
968286 || 49001645 ||
987218 || 00048038 || 49001401 ||
999999 || 99999999 || 99999998 || 99999997 || 99999996 ||
There are clients with more products than 4 in the example shown above so I would like the query to be expandable by counting # of product IDs in each client ID.
I took the code from ChrisPadgham's post and modified it to meet my needs...Here is what I have so far but it is not going further than PRODUC1:
TRANSFORM Last([PRODUCT_ID]) AS Details
SELECT Source_Table.CLI_ID
FROM Source_Table
GROUP BY Source_Table.CLI_ID
PIVOT "PRODUC" & (DCount("[PRODUCT_ID]","[Source_Table]", "[PRODUCT_ID]<>" &
[PRODUCT_ID] & " AND [CLI_ID]=" & [CLI_ID]) +1);
Any help would be appreciated!
Try adding a per client item counter to your data. Examples Found Here
It looks like your going to need to create a temp table as Access can't keep track of internal pointers when nesting the queries.
Create a temp table:
SELECT t1.CLI_ID, t1.PRODUCT_ID, (SELECT COUNT(*)
FROM Source_Table t2
WHERE t2.CLI_ID = t1.CLI_ID
AND t2.PRODUCT_ID <= t1.PRODUCT_ID
) AS PROD_COUNT INTO TEMP_CLI_PROD
FROM Source_Table AS t1
GROUP BY t1.CLI_ID, t1.PRODUCT_ID;
Then have your pivot table reference the temp table.
TRANSFORM Last(TEMP_CLI_PROD.PRODUCT_ID) AS LastOfPRODUCT_ID
SELECT TEMP_CLI_PROD.CLI_ID
FROM TEMP_CLI_PROD
GROUP BY TEMP_CLI_PROD.CLI_ID
PIVOT "PRODUCT " & TEMP_CLI_PROD.PROD_COUNT;
Output:
CLI_ID PRODUCT 1 PRODUCT 2 PRODUCT 3 PRODUCT 4
963506 49001608
968286 49001645
987218 00048038 49001401
999999 9999996 9999997 9999998 9999999
Jeff's answer is essentially correct in its approach, although a temporary table is not strictly required:
TRANSFORM First(PRODUCT_ID) AS whatever
SELECT CLI_ID
FROM
(
SELECT t1.CLI_ID, t1.PRODUCT_ID, 'PRODUCT_' & COUNT(*) AS XtabColumn
FROM
Source_Table AS t1
INNER JOIN
Source_Table AS t2
ON t1.CLI_ID = t2.CLI_ID
AND t1.PRODUCT_ID >= t2.PRODUCT_ID
GROUP BY t1.CLI_ID, t1.PRODUCT_ID
)
GROUP BY CLI_ID
PIVOT XtabColumn
returns
CLI_ID PRODUCT_1 PRODUCT_2 PRODUCT_3 PRODUCT_4
------ --------- --------- --------- ---------
963506 49001608
968286 49001645
987218 00048038 49001401
999999 9999996 9999997 9999998 9999999

linking the query in the select statement

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