简体   繁体   English

在 SQL*Plus 报告中格式化输出(假脱机到 CSV 之后)

[英]Formatting output in a report on SQL*Plus (after spool to CSV)

I have a question about formatting the output in a report that I get from csv that was spooled from SQL*Plus.我对从从 SQL*Plus 假脱机的 csv 获得的报告中的输出格式化有疑问。 I have Initial table whose data looks like this:我有初始表,其数据如下所示:

ord_no订单号 ls_prod_division ls_prod_division ls_prod_area ls_prod_area ls_prod_family_name ls_prod_family_name ls_prod_family_code ls_prod_family_code ls_prod_generic_name ls_prod_generic_name ls_brand_name ls_brand_name ls_reference_prod ls_reference_prod ls_description ls_description ls_atc_code ls_atc_code ls_atc_desc ls_atc_desc ls_indication ls_indication ls_ind_meddra_lvl ls_ind_meddra_lvl ls_ind_meddra_ver ls_ind_meddra_ver ls_ind_meddra_code ls_ind_meddra_code ls_cindication ls_cindication ls_psur_int_birthday ls_psur_int_birthday ls_dsur_int_birth_date ls_dsur_int_birth_date ls_eu_birth_date ls_eu_birth_date ls_psur_reference_date ls_psur_reference_date ls_psur_type ls_psur_type ls_psur_sub_freq_value ls_psur_sub_freq_value ls_psur_sub_freq_unit ls_psur_sub_freq_unit ls_date_psur_start ls_date_psur_start ls_date_psur_end ls_date_psur_end ls_psur_subm_due_date ls_psur_subm_due_date
1 1个 Medicinal Product医药产品 Aceclofenac醋氯芬酸 Aceclofenac醋氯芬酸 Aceclofenac 1.5% w/w cream醋氯芬酸 1.5% w/w 乳膏 M01AB16 M01AB16 aceclofenac醋氯芬酸 Analgesic & Anti-inflammatory镇痛消炎
1 1个 Medicinal Product医药产品 Aceclofenac醋氯芬酸 Aceclofenac醋氯芬酸 Aceclofenac 100 mg tablets醋氯芬酸 100 毫克片剂 M01AB16 M01AB16 aceclofenac醋氯芬酸 NSAIDS非甾体抗炎药
1 1个 Medicinal Product医药产品 Aceclofenac醋氯芬酸 Aceclofenac醋氯芬酸 Aceclofenac 100 mg tablets醋氯芬酸 100 毫克片剂 M01AB16 M01AB16 aceclofenac醋氯芬酸 Anti-inflammatory消炎(药
1 1个 Medicinal Product医药产品 Aceclofenac醋氯芬酸 Aceclofenac醋氯芬酸 Aceclofenac 100 mg tablets醋氯芬酸 100 毫克片剂 M01 M01 ANTIINFLAMMATORY AND ANTIRHEUMATIC PRODUCTS抗炎和抗风湿产品 Antiinflammatory消炎(药

And I am trying to format the report output of that table so I get it like this:我正在尝试格式化该表的报告输出,所以我得到它是这样的:

ord_no订单号 ls_prod_division ls_prod_division ls_prod_area ls_prod_area ls_prod_family_name ls_prod_family_name ls_prod_family_code ls_prod_family_code ls_prod_generic_name ls_prod_generic_name ls_brand_name ls_brand_name ls_reference_prod ls_reference_prod ls_description ls_description ls_atc_code ls_atc_code ls_atc_desc ls_atc_desc ls_indication ls_indication ls_ind_meddra_lvl ls_ind_meddra_lvl ls_ind_meddra_ver ls_ind_meddra_ver ls_ind_meddra_code ls_ind_meddra_code ls_cindication ls_cindication ls_psur_int_birthday ls_psur_int_birthday ls_dsur_int_birth_date ls_dsur_int_birth_date ls_eu_birth_date ls_eu_birth_date ls_psur_reference_date ls_psur_reference_date ls_psur_type ls_psur_type ls_psur_sub_freq_value ls_psur_sub_freq_value ls_psur_sub_freq_unit ls_psur_sub_freq_unit ls_date_psur_start ls_date_psur_start ls_date_psur_end ls_date_psur_end ls_psur_subm_due_date ls_psur_subm_due_date
1 1个 Medicinal Product医药产品 Aceclofenac醋氯芬酸 Aceclofenac醋氯芬酸 Aceclofenac 1.5% w/w cream醋氯芬酸 1.5% w/w 乳膏 M01AB16 M01AB16 aceclofenac醋氯芬酸 Analgesic & Anti-inflammatory镇痛消炎
1 1个 Aceclofenac 100 mg tablets醋氯芬酸 100 毫克片剂 M01 M01 ANTIINFLAMMATORY AND ANTIRHEUMATIC PRODUCTS抗炎和抗风湿产品 NSAIDS非甾体抗炎药
1 1个 Anti-inflammatory消炎(药
1 1个 Antiinflammatory消炎(药

I am using the following code:我正在使用以下代码:

set colsep '|'
set trimspool on
set termout off
set echo off
set trim on
set heading on
set feedback off
set linesize 32000
set trimout on
set pagesize 50000
set underline off
col ord_no format 99999
col ls_prod_area format a200
col ls_prod_family_name format a200
col ls_prod_family_code format a200
col ls_prod_generic_name format a200
col ls_brand_name format a200
col ls_reference_prod format a200
col ls_description format a200
col ls_atc_code format a200
col ls_atc_desc format a200
col ls_indication format a200
col ls_ind_meddra_lvl format a200
col ls_ind_meddra_ver format a200
col ls_ind_meddra_code format a200
spool export.csv
break on ls_prod_area on ls_prod_family_name on ls_prod_generic_name on ls_atc_code on ls_atc_desc on ls_brand_name on ls_indication

SELECT rpad(ord_no, 200, ' ') ord_no ,rpad(ls_prod_area, 200, ' ') ls_prod_area ,rpad(ls_prod_family_name, 200, ' ') ls_prod_family_name ,rpad(ls_prod_family_code, 200, ' ') ls_prod_family_code ,rpad(ls_prod_generic_name, 200, ' ') ls_prod_generic_name ,rpad(ls_brand_name, 200, ' ') ls_brand_name ,rpad(ls_reference_prod, 200, ' ') ls_reference_prod,rpad(ls_description, 200, ' ') ls_description ,rpad(ls_atc_code, 200, ' ') ls_atc_code ,rpad(ls_atc_desc, 200, ' ') ls_atc_desc ,rpad(ls_indication, 200, ' ') ls_indication ,rpad(ls_ind_meddra_lvl, 200, ' ') ls_ind_meddra_lvl ,rpad(ls_ind_meddra_ver, 200, ' ') ls_ind_meddra_ver ,rpad(ls_ind_meddra_code, 200, ' ') ls_ind_meddra_code 
from tmp_product_family
order by ord_no, ls_brand_name,LS_ATC_CODE, LS_ATC_DESC, LS_INDICATION, LS_IND_MEDDRA_LVL, LS_IND_MEDDRA_VER, LS_IND_MEDDRA_CODE ;

And I don't have a problem with output in CMD window, but with transforming the CSV to Excel via Excel's Data From Text/CSV tool.我在 CMD 窗口中的输出没有问题,但是通过 Excel 的文本/CSV 数据工具将 CSV 转换为 Excel。 I am putting custom delimiter which is set to '|'.我将自定义分隔符设置为“|”。

And for the first ord_no (1) first and second row that were transformed from CSV to Excel looks like this:对于第一个ord_no (1),从 CSV 转换为 Excel 的第一行和第二行如下所示:

ORD_NO ORD_NO LS_PROD_AREA LS_PROD_AREA LS_PROD_FAMILY_NAME LS_PROD_FAMILY_NAME LS_PROD_FAMILY_CODE LS_PROD_FAMILY_CODE LS_PROD_GENERIC_NAME LS_PROD_GENERIC_NAME LS_BRAND_NAME LS_BRAND_NAME LS_REFERENCE_PROD LS_REFERENCE_PROD LS_DESCRIPTION LS_DESCRIPTION 说明 LS_ATC_CODE LS_ATC_CODE LS_ATC_DESC LS_ATC_DESC LS_INDICATION LS_INDICATION 指示 LS_IND_MEDDRA_LVL LS_IND_MEDDRA_LVL LS_IND_MEDDRA_VER LS_IND_MEDDRA_VER LS_IND_MEDDRA_CODE LS_IND_MEDDRA_CODE
1 1个 Medicinal Product医药产品 Aceclofenac醋氯芬酸 Aceclofenac醋氯芬酸 Aceclofenac 1.5% w/w cream醋氯芬酸 1.5% w/w 乳膏 M01AB16 M01AB16 aceclofenac醋氯芬酸 Analgesic & Anti-inflammatory镇痛消炎
1 1个 Aceclofenac 100 mg tablets醋氯芬酸 100 毫克片剂 M01 M01 ANTIINFLAMMATORY AND ANTIRHEUMATIC PRODUCTS抗炎和抗风湿产品 Antiinflammatory消炎(药

As you can see in second row all the values after the ord_no are shifted three columns to the left (eg Aceclofenac 100 mg tablets should be in LS_BRAND_NAME column).正如您在第二行中看到的, ord_no之后的所有值都向左移动了三列(例如,Aceclofenac Aceclofenac 100 mg tablets应该在LS_BRAND_NAME列中)。

Does anyone have any idea how to escape that problem.有谁知道如何避免这个问题。

As you asked about break , this is what I meant.正如您询问break ,这就是我的意思。

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 eg 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:如果您在 Excel 中打开该文件,您会得到:

  1. in ver.在版本中。 2016, go to "Data - From text" and follow the wizard 2016,转到“数据 - 来自文本”并按照向导操作
  2. choose "Fixed width"选择“固定宽度”
  3. move separator lines where necessary必要时移动分隔线
  4. see the result查看结果

在此处输入图像描述


Connecting to SQL*Plus连接到 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 ).例如,我正在使用mctnsping实用程序扫描我的 TNSNAMES.ORA 文件(由 Michel Cadot 编写;它不需要 Oracle 客户端即可工作。您可以在OraFAQ 论坛上找到它)。 Or, if you have TNSPING available, use it:或者,如果您有可用的 TNSPING,请使用它:

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 :连接字符串的格式为@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>

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 C D
A1 A1 B1 B1 C1 C1 D1 D1
A2 A2 B2 B2 C2 C2 null无效的
A3 A3 B3 B3 null无效的 null无效的
A4 A4 null无效的 null无效的 null无效的

fiddle小提琴

I actually managed to do it by changing the delimiter from '|'我实际上是通过更改“|”中的分隔符来做到这一点的to Fixed width of 0, 800, 1000, 1200 etc. That worked fine now so thank you all for your effort.固定宽度为 0、800、1000、1200 等。现在效果很好,谢谢大家的努力。 I need a little bit more tuning with the break on command but I think this solved all my future reporting problems.我需要对 break on 命令进行更多调整,但我认为这解决了我未来所有的报告问题。 Huge thanks to @Littlefoot, kudos for making my life easier.非常感谢@Littlefoot,让我的生活更轻松的荣誉。

Solved import from Text/CSV解决了从文本/CSV 导入

Additionally to using SQL*Plus and break on command, I managed to the same thing in SQL Developer with LAG function and some partition by clause, and then comparing column values to previous rows.除了使用 SQL*Plus 和 break on 命令外,我还设法在 SQL Developer 中使用 LAG 函数和一些 partition by 子句完成同样的事情,然后将列值与前面的行进行比较。

Something like this:像这样:

SELECT ord_no,case when ls_prod_area=prev_ls_prod_area then null else ls_prod_area end,case when ls_prod_family_name=prev_ls_prod_family_name then null else ls_prod_family_name end,ls_prod_family_code,case when ls_prod_generic_name=prev_ls_prod_generic_name then null else ls_prod_generic_name end,case when ls_brand_name=prev_ls_brand_name then null else ls_brand_name end,ls_reference_prod,ls_description,case when ls_atc_code=prev_ls_atc_code then null else ls_atc_code end,case when ls_atc_desc=prev_ls_atc_desc then null else ls_atc_desc end,case when ls_indication=prev_ls_indication then null else ls_indication end,case when ls_ind_meddra_lvl=prev_ls_ind_meddra_lvl then null else ls_ind_meddra_lvl end,case when ls_ind_meddra_ver=prev_ls_ind_meddra_ver then null else ls_ind_meddra_ver end,case when ls_ind_meddra_code=prev_ls_ind_meddra_code then null else ls_ind_meddra_code end
  FROM (
SELECT ord_no,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,
       LAG( ls_prod_area, 1 ) OVER ( PARTITION BY ord_no ORDER BY ord_no, ls_brand_name, LS_ATC_CODE desc, LS_ATC_DESC, LS_INDICATION, LS_IND_MEDDRA_LVL nulls last, LS_IND_MEDDRA_VER nulls last, LS_IND_MEDDRA_CODE) AS prev_ls_prod_area,
       LAG( ls_prod_family_name, 1 ) OVER ( PARTITION BY ord_no ORDER BY ord_no, ls_brand_name, LS_ATC_CODE desc, LS_ATC_DESC, LS_INDICATION, LS_IND_MEDDRA_LVL nulls last, LS_IND_MEDDRA_VER nulls last, LS_IND_MEDDRA_CODE ) AS prev_ls_prod_family_name,
       LAG( ls_prod_generic_name, 1 ) OVER ( PARTITION BY ord_no ORDER BY ord_no, ls_brand_name, LS_ATC_CODE desc, LS_ATC_DESC, LS_INDICATION, LS_IND_MEDDRA_LVL nulls last, LS_IND_MEDDRA_VER nulls last, LS_IND_MEDDRA_CODE ) AS prev_ls_prod_generic_name,
        LAG( ls_brand_name, 1 ) OVER ( PARTITION BY ord_no ORDER BY ord_no, ls_brand_name, LS_ATC_CODE desc, LS_ATC_DESC, LS_INDICATION, LS_IND_MEDDRA_LVL nulls last, LS_IND_MEDDRA_VER nulls last, LS_IND_MEDDRA_CODE ) AS prev_ls_brand_name,
        LAG( ls_atc_code, 1 ) OVER ( PARTITION BY ord_no ORDER BY ord_no, ls_brand_name, LS_ATC_CODE desc, LS_ATC_DESC, LS_INDICATION, LS_IND_MEDDRA_LVL nulls last, LS_IND_MEDDRA_VER nulls last, LS_IND_MEDDRA_CODE ) AS prev_ls_atc_code,
        LAG( ls_atc_desc, 1 ) OVER ( PARTITION BY ord_no ORDER BY ord_no, ls_brand_name, LS_ATC_CODE desc, LS_ATC_DESC, LS_INDICATION, LS_IND_MEDDRA_LVL nulls last, LS_IND_MEDDRA_VER nulls last, LS_IND_MEDDRA_CODE ) AS prev_ls_atc_desc,
        LAG( ls_indication, 1 ) OVER ( PARTITION BY ord_no ORDER BY ord_no, ls_brand_name, LS_ATC_CODE desc, LS_ATC_DESC, LS_INDICATION, LS_IND_MEDDRA_LVL nulls last, LS_IND_MEDDRA_VER nulls last, LS_IND_MEDDRA_CODE ) AS prev_ls_indication,
        LAG( ls_ind_meddra_lvl, 1 ) OVER ( PARTITION BY ord_no ORDER BY ord_no, ls_brand_name, LS_ATC_CODE desc, LS_ATC_DESC, LS_INDICATION, LS_IND_MEDDRA_LVL nulls last, LS_IND_MEDDRA_VER nulls last, LS_IND_MEDDRA_CODE ) AS prev_ls_ind_meddra_lvl,
        LAG( ls_ind_meddra_ver, 1 ) OVER ( PARTITION BY ord_no ORDER BY ord_no, ls_brand_name, LS_ATC_CODE desc, LS_ATC_DESC, LS_INDICATION, LS_IND_MEDDRA_LVL nulls last, LS_IND_MEDDRA_VER nulls last, LS_IND_MEDDRA_CODE ) AS prev_ls_ind_meddra_ver,
        LAG( ls_ind_meddra_code, 1 ) OVER ( PARTITION BY ord_no ORDER BY ord_no, ls_brand_name, LS_ATC_CODE desc, LS_ATC_DESC, LS_INDICATION, LS_IND_MEDDRA_LVL nulls last, LS_IND_MEDDRA_VER nulls last, LS_IND_MEDDRA_CODE ) AS prev_ls_ind_meddra_code
        FROM tmp_product_family);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM