0

I have below anonymous block where i am using cursor to generate the resultset and save it into TEST_REPORT.csv file. But i am getting error on the line where i am using spool as:

PLS-00103: Encountered the symbol "H" when expecting one of the following:
   := . ( @ % ;

I believe i cannot use spool in PL/SQL but not sure so i have tried below code. And also i cannot use UTL_FILE or UTL_FILE_DIR logic because of security restriction on the Production. As employees works on different department i want to generate separate csv file for each employee with respect to their department.

Is there anyway where i can break this code and use spool to generate csv file or any other logic ? Also if it is not possible to use spool in anonymous block then can i use it during execution of this code to generate files ?

If its not possible using spool then is it possible if i can organize my query result in such a way that it will be easy to export the result into single csv file after executing this anonymous block and then i can separate the single csv file into multiple files depending on the employee with their department manually ?

Generate-And-Run.sql file

SET SERVEROUTPUT ON;
set verify off
SET LONG 100000
SET lines 1000 
SET sqlformat SELECT;
SPOOL C:\Loop-Flattener.sql;

PROMPT VAR V_A VARCHAR2(64);


BEGIN
  FOR TARGET_POINTER IN (select ID,
       name,
       ST_ID      
from TEST_REPORT
where rownum <5)
    LOOP
    DBMS_OUTPUT.PUT_LINE('DEFINE TARGET = '''||TARGET_POINTER.ID||''';');
    DBMS_OUTPUT.PUT_LINE('EXEC :V_A := '''||TARGET_POINTER.ID||'''; ');
    DBMS_OUTPUT.PUT_LINE('@@Target-Csv-Generator.sql;');
  END LOOP;
END;
/

SPOOL OFF;
Andrew
  • 3,632
  • 24
  • 64
  • 113

2 Answers2

1

As OldProgrammer mentioned, you cannot call SPOOL within PL/SQL. But you can can print to a spooled file from within PL/SQL, and can pre-compile/flatten the loop into a delegating intermediate worker-script that repeatedly calls the csv-generator.

Update. In response to the specifics you provided, the following should work for you.

Example Test data:

INSERT INTO CSS_BOUTIQUE VALUES ('EUR', 'Belgium', 'a0Hb0000006LLdQ');
INSERT INTO CSS_BOUTIQUE VALUES ('EUR', 'Portugal', 'a0HB0000006LLOG');
INSERT INTO CSS_BOUTIQUE VALUES ('EUR', 'Portugal', 'a0HB0000006LLYu');

INSERT INTO CSS_BOUTIQUE VALUES ('ASIA', 'Korea', 'e0HB0000016MEIi');
INSERT INTO CSS_BOUTIQUE VALUES ('ASIA', 'China', 'e0HB0000026MEIi');
INSERT INTO CSS_BOUTIQUE VALUES ('ASIA', 'Japan', 'e0HB0000036MEIi');

INSERT INTO CSS_BOUTIQUE VALUES ('SA', 'Chile', 's0HB0000016MEIi');
INSERT INTO CSS_BOUTIQUE VALUES ('SA', 'Argentina', 's0HB0000026MEIi');
INSERT INTO CSS_BOUTIQUE VALUES ('SA', 'Equador', 's0HB0000036MEIi');

Then create the following two scripts:

Generate-And-Run.sql This script will create a flat, pseudo-loop by generating an intermediate script filled with iterative commands to set new variable names and call a reusable csv-generator.

SET ECHO OFF;
SET FEEDBACK OFF;
SET HEAD OFF;
SET LIN 256;
SET TRIMSPOOL ON;
SET WRAP OFF;
SET PAGES 0;
SET TERM OFF;
SET SERVEROUTPUT ON;

SPOOL Loop-Flattener.sql;

PROMPT VAR V_ZONE_NAME VARCHAR2(64);


BEGIN
  FOR TARGET_POINTER IN (SELECT DISTINCT ZONE FROM CSS_BOUTIQUE)
    LOOP
    DBMS_OUTPUT.PUT_LINE('DEFINE TARGET = '''||TARGET_POINTER.ZONE||''';');
    DBMS_OUTPUT.PUT_LINE('EXEC :V_ZONE_NAME := '''||TARGET_POINTER.ZONE||'''; ');
    DBMS_OUTPUT.PUT_LINE('@@Target-Csv-Generator.sql;');
  END LOOP;
END;
/

SPOOL OFF;

@@Loop-Flattener.sql;

Target-Csv-Generator.sql: This script will do the work of generating a single csv. Please note, the report-name here is a simple REPORT_FOR... without any additional path to help ensure it gets created in the working directory.

SPOOL REPORT_FOR_&&TARGET..csv;

PROMPT zone,market, boutique_id;

select zone||','||
       market||','||
       boutique_id      
from CSS_BOUTIQUE
where rownum <5 and ZONE = :V_ZONE_NAME;

SPOOL OFF;

Then run it:

Place the above two scripts into the directory where you want your CSVs to be created, then Start SQLPlus in that directory

SQL*Plus: Release 12.2.0.1.0 Production on Sun Sep 10 14:38:13 2017
SQL> @@Generate-And-Run

Now, the working-directory has three new files:

REPORT_FOR_EUR.csv
REPORT_FOR_SA.csv
REPORT_FOR_ASIA.csv

And each only has the data for its zone. For example:

cat REPORT_FOR_ASIA.csv 

Yields:

zone,market, boutique_id
ASIA,Korea,e0HB0000016MEIi
ASIA,China,e0HB0000026MEIi
ASIA,Japan,e0HB0000036MEIi
alexgibbs
  • 2,430
  • 2
  • 16
  • 18
  • i have tried your method which is very helpful and generating file with query result. I have mentioned my code in question. But the only thing is i have to create separate file now for each employe with respect to their department. Currently its printing everything in one file. Thats why i want to use loop in code – Andrew Sep 04 '17 at 07:56
  • Thanks @Andrew That is interesting. Good news is you can still use sqlplus for this by flattening the loop. Rather than one script with one spool, you can use two -- a reusable script like these to spool details, that uses a substitution variable for file name and bind(s) for query params, and either of the above approaches can also be used to spool out a first master-script that flattens the loop-- it sets the query-param and file-name for each target and calls the reusable script in a big list. I'm away from my computer until tomorrow but can update with a working example then. Thanks – alexgibbs Sep 04 '17 at 14:37
  • thanks for your response Alex. I will be waiting for your working example meanwhile i will also give try for the same. And i would also like to know as i dont have access to sqlplus. Can i use the logic which you are talking in sql developer ? – Andrew Sep 05 '17 at 06:17
  • I found one example here. Did you mean the same way ? https://stackoverflow.com/questions/25184216/automatically-extracting-data-oracle-sql-developer Also i want to parameterized in my case ID,department_name and application_date. – Andrew Sep 05 '17 at 06:50
  • Thanks @Andrew. Yeah the referenced question has a similar approach--auto-generating a wrapper script, then calling the wrapper. This is the sort of thing I was referring to with compiling the loop. I'll update with the example now. Thanks – alexgibbs Sep 05 '17 at 15:21
  • @Andrew the example has been updated. Does this approach work for you? – alexgibbs Sep 05 '17 at 15:31
  • Hello Alex thanks for your response which is indeed i am needed. I have tried your method the same way changing my query but its not generating the excel file – Andrew Sep 05 '17 at 20:49
  • I think the bind variable is not getting assigned to file Target-Csv-Generator.sql – Andrew Sep 05 '17 at 21:06
  • Also i think this code will generate the excel file for every row which the Generate-And-Run.sql file returns right ? Because for example i need to create and filter this depending on the unique department. So one department might have many employees – Andrew Sep 05 '17 at 21:14
  • Thanks @Andrew. This example creates one csv per row, but you can aggregate the data and parameterize by nondistinct values like a `department_id`. It is hard to guess what is happening without seeing the data involved. Can you update with the structure of `EMPLYOEE_MARKET` with a little test data, and maybe the planned output csvs from the test data? I can take a look at the specifics. Thanks – alexgibbs Sep 05 '17 at 21:35
  • can we discuss this on chat where i will show you my code which i am trying ? When i run @Generate-And-Run its generating below code in Loop-Flattener.sql : VAR V_A VARCHAR2(64) anonymous block completed DEFINE TARGET = 'AU'; EXEC :V_A := 'AU'; C:\CKLR_REPORT\Target-Csv-Generator.sql; DEFINE TARGET = 'AU'; EXEC :V_A := 'AU'; C:\CKLR_REPORT\Target-Csv-Generator.sql; DEFINE TARGET = 'AU'; EXEC :V_A := 'AU'; C:\CKLR_REPORT\Target-Csv-Generator.sql; – Andrew Sep 06 '17 at 06:20
  • But i dont know for what reason the csv is not getting created in directory. I think its not calling the Target-Csv-Generator.sql properly not sure. If it create csv then my problem will get solved and like you said i will put restrictions later on. If i put :V_A vaule directly in Target-Csv-Generator.sql then its creating excel file. I am running this code in sql developer is it the problem because of that ? – Andrew Sep 06 '17 at 06:21
  • Thanks @Andrew The raw `C:\CKLR_REPORT\Target-Csv-Generator.sql;` in the content of `Loop-Flattener.sql` worries me. SQLPlus should not run that without an `@` or `@@` in front of the Csv-Generator script. (in my answer I ran SQLPlus in a work directory with `@@Target-Csv-Generator.sql;`). Can you try putting things in a work-directory and dropping the full paths C:\CKLR.... and just running SQLPlus from the directory, calling local file `@@Target-Csv-Generater.sql' as in the example and let me know if this helps? SQLDeveloper should be ok. Sure we can chat, I'll be around for a few hrs. – alexgibbs Sep 07 '17 at 16:50
  • Thanks Alex. Actually in my previous try i tried putting @@Target-Csv-Generator.sql also in the working directory but still its not generating the excel file. Because i am using working directory for this reason its generating the Loop-Flattener.sql file but not excel file in that directory. I think i am doing small mistake soemwhere but not able to identify. Can i share my code? But i dont know how can i share ? By looking into code might be easy for you to identify the bug. – Andrew Sep 07 '17 at 20:56
  • Thanks @Andrew sure thing you can post up the code and I can take a look. You can update the question with some example `EMPLYOEE_MARKET` data and the current spooling code and we can go from there. It would be good to confirm whether the throwaway example `ABC` works in its current form for you as well. Thanks – alexgibbs Sep 08 '17 at 19:05
  • Thanks Alex. I have edited my question and mentioned the example which i am trying in working directory. Actually its not generating excel file. The example which i am trying is different than employee_market data. I have also given the select query result data from @@Target-Csv-Generator.sql . I think there is minor bug somewhere which is not generating excel file. And yes in the previous example which you mentioned using for loop using PL/SQL it works and generate excel file for the same query. – Andrew Sep 10 '17 at 18:05
  • Thanks @Andrew Ok looking into this now. I'll update my post with something verified to be functional, but at a glance, I'd be suspicious of the `SPOOL H:\TEST_REPORT\REPORT_FOR_&&TARGET..csv;`. SQLPlus deals with paths relative to where the SQLPlus was itself launched, or relative to the current script ( `@` vs `@@`). So it could be trying to create an illegal windows-file. – alexgibbs Sep 10 '17 at 20:27
  • Ok @Andrew I tossed the original examples and added a solution that works with your specific tables/queries. To keep things simple, it requires that you put the scripts in the same directory where you run SQLPlus. Can you try it out and let me know how that goes? Thanks – alexgibbs Sep 10 '17 at 20:48
  • Thanks Alex it works superbly now. I think the only thing in previously code it was missing calling the @@Loop-Flattener.sql; in Generate-And-Run.sql. But it works now Thanks for your precious time and help. It really helped a lot instead of creating directory in oracle and other things which i cannot do because of security issue from client side – Andrew Sep 11 '17 at 07:29
  • Great to hear @Andrew ! And no worries--I understand using UTL_FILE can be a problem sometimes. If you feel your question has been answered, please consider accepting an answer. If any other questions just let me know. Thanks – alexgibbs Sep 11 '17 at 11:32
0

You cannot call spool within a pl/sql block Spool is a sqlplus command, not pl/sql. Move the statement to before the DECLARE statement.

OldProgrammer
  • 12,050
  • 4
  • 24
  • 45