Home > Software design >  Arranging data to given format in SQL
Arranging data to given format in SQL

Time:12-21

I have a question about a problem I am dealing with for the past couple days. Unfortunately, posting here is my last resort as I am running out of options. I have sample of data that looks like this:

ord_no ls_prod_division ls_prod_area ls_prod_family_name ls_prod_family_code ls_prod_generic_name ls_brand_name ls_reference_prod ls_description ls_atc_code ls_atc_desc ls_indication ls_ind_meddra_lvl ls_ind_meddra_ver ls_ind_meddra_code ls_cindication ls_psur_int_birthday ls_dsur_int_birth_date ls_eu_birth_date ls_psur_reference_date ls_psur_type ls_psur_sub_freq_value ls_psur_sub_freq_unit ls_date_psur_start ls_date_psur_end ls_psur_subm_due_date
1 Medicinal Product Aceclofenac Aceclofenac Aceclofenac 1.5% w/w cream M01AB16 aceclofenac Analgesic & Anti-inflammatory
1 Medicinal Product Aceclofenac Aceclofenac Aceclofenac 100 mg tablets M01AB16 aceclofenac NSAIDS
1 Medicinal Product Aceclofenac Aceclofenac Aceclofenac 100 mg tablets M01AB16 aceclofenac Anti-inflammatory
1 Medicinal Product Aceclofenac Aceclofenac Aceclofenac 100 mg tablets M01 ANTIINFLAMMATORY AND ANTIRHEUMATIC PRODUCTS Antiinflammatory

I need it to look like this for all the data (sample of arranged data is just for ord_no 1):

ord_no ls_prod_division ls_prod_area ls_prod_family_name ls_prod_family_code ls_prod_generic_name ls_brand_name ls_reference_prod ls_description ls_atc_code ls_atc_desc ls_indication ls_ind_meddra_lvl ls_ind_meddra_ver ls_ind_meddra_code ls_cindication ls_psur_int_birthday ls_dsur_int_birth_date ls_eu_birth_date ls_psur_reference_date ls_psur_type ls_psur_sub_freq_value ls_psur_sub_freq_unit ls_date_psur_start ls_date_psur_end ls_psur_subm_due_date
1 Medicinal Product Aceclofenac Aceclofenac Aceclofenac 1.5% w/w cream M01AB16 aceclofenac Analgesic & Anti-inflammatory
1 Aceclofenac 100 mg tablets M01 ANTIINFLAMMATORY AND ANTIRHEUMATIC PRODUCTS NSAIDS
1 Anti-inflammatory
1 Antiinflammatory

I am trying to get it done through Oracle SQL but I am having trouble with rows merging without having rows with null values.

This is SQL I am using:

select ord_no, PRODUCT_AREA, PRODUCT_FAMILY_NAME, PRODUCT_FAMILY_CODE, PRODUCT_GENERIC_NAME, BRAND_NAME, REFERENCE_PRODUCT,DESCRIPTION, ATC_CODE, ATC_DESCRIPTION, INDICATION, MEDDRA_LEVEL, MEDDRA_VERSION,MEDDRA_CODE from (with a as (select distinct LS_PROD_FAMILY_NAME,LS_PROD_GENERIC_NAME, ord_no, 1 as flag from tmp_product_family),
b as (select distinct LS_BRAND_NAME, ord_no, 2 as flag from tmp_product_family where LS_PROD_FAMILY_NAME is not null),
c as (select distinct LS_ATC_CODE, LS_ATC_DESC, ord_no, 3 as flag from tmp_product_family where LS_ATC_DESC is not null and LS_ATC_CODE is not null),
d as (select distinct LS_INDICATION, LS_IND_MEDDRA_LVL, LS_IND_MEDDRA_VER, LS_IND_MEDDRA_CODE, ord_no, 4 as flag from tmp_product_family where LS_INDICATION is not null)
select 'Medicinal product' as product_area, a.LS_PROD_FAMILY_NAME as Product_family_name, null as Product_family_code
, a.LS_PROD_GENERIC_NAME as Product_generic_name,
null as brand_name, null as reference_product, null as Description, null as atc_code, null as atc_description, null as indication , null as MedDRA_level,
null as meddra_version,null as meddra_code, a.ord_no, a.flag from a
UNION
select null as product_area, null as Product_family_name, null as Product_family_code
, null as Product_generic_name,
b.LS_BRAND_NAME as brand_name, null as reference_product, null as Description, null as atc_code, null as atc_description, null as indication , null as MedDRA_level,
null as meddra_version,null as meddra_code, b.ord_no, b.flag from b
UNION
select null as product_area, null as Product_family_name, null as Product_family_code
, null as Product_generic_name,
null as brand_name, null as reference_product, null as Description, c.LS_ATC_CODE as atc_code, c.LS_ATC_DESC as atc_description, null as indication , null as MedDRA_level,
null as meddra_version,null as meddra_code, c.ord_no, c.flag from c
UNION
select null as product_area, null as Product_family_name, null as Product_family_code
, null as Product_generic_name,
null as brand_name, null as reference_product, null as Description, null as atc_code, null as atc_description, d.LS_INDICATION as indication , d.LS_IND_MEDDRA_LVL as MedDRA_level,
d.LS_IND_MEDDRA_VER as meddra_version,d.LS_IND_MEDDRA_CODE as meddra_code, d.ord_no, d.flag from d) order by ord_no, flag;

And this is the result set I am getting:

ord_no ls_prod_division ls_prod_area ls_prod_family_name ls_prod_family_code ls_prod_generic_name ls_brand_name ls_reference_prod ls_description ls_atc_code ls_atc_desc ls_indication ls_ind_meddra_ver ls_ind_meddra_code ls_cindication ls_psur_int_birthday ls_dsur_int_birth_date ls_eu_birth_date ls_psur_reference_date ls_psur_type ls_psur_sub_freq_value ls_psur_sub_freq_unit ls_date_psur_start ls_date_psur_end ls_psur_subm_due_date
1 Medicinal product Aceclofenac Aceclofenac
1 Aceclofenac 100 mg tablets
1 Aceclofenac 1.5% w/w cream
1 M01 ANTIINFLAMMATORY AND ANTIRHEUMATIC PRODUCTS
1 M01AB16 aceclofenac
1 Anti-inflammatory
1 Antiinflammatory
1 NSAIDS
1 Analgesic & Anti-inflammatory

I want the ls_brand_name, ls_atc_code, ls_atc_desc column values to be in first and second row, and ls_indication column values to be populated in rows 1,2,3 and 4 for ord_no 1. That would mean I have 4 rows for ord_no 1 which would then be formatted as it should be.

Does anyone have any ideas?

Thanks a lot for your help.

CodePudding user response:

As you asked about break, this is what I meant.

An ordinary query, where all "cells" are populated with data:

SQL> select d.dname, e.job, e.ename, e.sal
  2  from emp e join dept d on d.deptno = e.deptno
  3  order by d.dname, e.job;

DNAME          JOB       ENAME             SAL
-------------- --------- ---------- ----------
ACCOUNTING     CLERK     MILLER           1300
ACCOUNTING     MANAGER   CLARK            2450
ACCOUNTING     PRESIDENT KING             5000
RESEARCH       ANALYST   SCOTT            3000
RESEARCH       ANALYST   FORD             3000
RESEARCH       CLERK     ADAMS            1100
RESEARCH       CLERK     SMITH             840
RESEARCH       MANAGER   JONES            2975
SALES          CLERK     JAMES             950
SALES          MANAGER   BLAKE            2850
SALES          SALESMAN  MARTIN           1250
SALES          SALESMAN  WARD             1250
SALES          SALESMAN  ALLEN            1600
SALES          SALESMAN  TURNER           1500

14 rows selected.

If you put a break on e.g. department name and job, you get something that looks like result you need:

SQL> spool stefek.csv
SQL> break on dname on job
SQL> select d.dname, e.job, e.ename, e.sal
  2  from emp e join dept d on d.deptno = e.deptno
  3  order by d.dname, e.job;

DNAME          JOB       ENAME             SAL
-------------- --------- ---------- ----------
ACCOUNTING     CLERK     MILLER           1300
               MANAGER   CLARK            2450
               PRESIDENT KING             5000
RESEARCH       ANALYST   SCOTT            3000
                         FORD             3000
               CLERK     ADAMS            1100
                         SMITH             840
               MANAGER   JONES            2975
SALES          CLERK     JAMES             950
               MANAGER   BLAKE            2850
               SALESMAN  MARTIN           1250
                         WARD             1250
                         ALLEN            1600
                         TURNER           1500

14 rows selected.

SQL> spool off
SQL>

If you open that file in Excel, you get this:

  1. in ver. 2016, go to "Data - From text" and follow the wizard
  2. choose "Fixed width"
  3. move separator lines where necessary
  4. see the result

enter image description here


Connecting to SQL*Plus

You should know username, password and database you're connecting to. For example, I'm scanning my TNSNAMES.ORA file with the mctnsping utility (written by Michel Cadot; it doesn't require Oracle client to work. You can find it on OraFAQ Forum). Or, if you have TNSPING available, use it:

C:\Temp>mctnsping orcl

McTnsping Utility by Michel Cadot: Version 2021.12.03 on 20-PRO-2022 14:09:02

Copyright (c) Michel Cadot, 2016-2021. All rights reserved.

Using ping version 11

Used parameter files:
C:\Users\littlefoot\Documents\sqlnet.ora
C:\Users\littlefoot\Documents\tnsnames.ora

Found tnsnames.ora entry:
(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=db_orcl)(PORT=1521))(CONNECT_DATA=(SID=orcl)))

Attempting to contact db_orcl:1521
OK (46 msec)

Now, use data you gathered; connect string is in format of @database_server:port/service_name:

C:\Temp>sqlplus scott/tiger@db_orcl:1521/orcl

SQL*Plus: Release 18.0.0.0.0 - Production on Uto Pro 20 14:09:17 2022
Version 18.5.0.0.0

Copyright (c) 1982, 2018, Oracle.  All rights reserved.


Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
With the Partitioning, Real Application Clusters, Automatic Storage Management, OLAP,
Data Mining and Real Application Testing options
   
SQL> select * from dual;

D
-
X

SQL>

CodePudding user response:

If you have the minimal sample data:

CREATE TABLE table_name (a, b, c, d ) AS
  SELECT 'A1', 'B1', 'C1', 'D1' FROM DUAL UNION ALL
  SELECT 'A2', 'B1', 'C2', 'D1' FROM DUAL UNION ALL
  SELECT 'A3', 'B2', 'C2', 'D1' FROM DUAL UNION ALL
  SELECT 'A4', 'B3', 'C2', 'D1' FROM DUAL;

and you want to shuffle the data up the rows so that duplicates are not displayed then you can rank the values in each column, unpivot and then re-pivot the data:

SELECT a, b, c, d
FROM   (
  SELECT a, b, c, d,
         DENSE_RANK() OVER (ORDER BY a) AS a_rnk,
         DENSE_RANK() OVER (ORDER BY b) AS b_rnk,
         DENSE_RANK() OVER (ORDER BY c) AS c_rnk,
         DENSE_RANK() OVER (ORDER BY d) AS d_rnk
  FROM   table_name
)
UNPIVOT (
  (value, rnk) FOR key IN (
    (a, a_rnk) AS 'A',
    (b, b_rnk) AS 'B',
    (c, c_rnk) AS 'C',
    (d, d_rnk) AS 'D'
  )
)
PIVOT (
  MAX(value) FOR key IN ('A' AS a, 'B' AS b, 'C' AS c, 'D' AS d)
)
ORDER BY rnk

Which outputs:

A B C D
A1 B1 C1 D1
A2 B2 C2 null
A3 B3 null null
A4 null null null

fiddle

  • Related