0

I'm trying to resolve below issue: I need to prepare table that consists 3 columns: user_id, month value. Each from over 200 users has got different values of parameters that determine expected value which are: LOB, CHANNEL, SUBSIDIARY. So I decided to store it in table ASYSTENT_GOALS_SET. But I wanted to avoid multiplying rows and thought it would be nice to put all conditions as a part of the code that I would use in "where" clause further in procedure. So, as an example - instead of multiple rows:

enter image description here

I created such entry: enter image description here

So far I created testing table ASYSTENT_TEST (where I collect month and value for certain user). I wrote a piece of procedure where I used BULK COLLECT.

declare
  type test_row is record
  (
  month NUMBER,
  value NUMBER
  );
  type test_tab is table of test_row;
  BULK_COLLECTOR test_tab;
  p_lob varchar2(10) :='GOSP';
  p_sub varchar2(14);
  p_ch varchar2(10) :='BR';
  begin
  select subsidiary into p_sub from ASYSTENT_GOALS_SET where user_id='40001001';
  execute immediate 'select mc, sum(ppln_wartosc) plan from prod_nonlife.mis_report_plans 
  where report_id = (select to_number(value) from prod_nonlife.view_parameters where view_name=''MIS'' and parameter_name=''MAX_REPORT_ID'')
    and year=2017 
    and month between 7 and 9 
    and ppln_jsta_symbol in (:subsidiary)
    and dcs_group in (:lob)
    and kanal in (:channel)
  group by month order by month' bulk collect into BULK_COLLECTOR
  using p_sub,p_lob,p_ch;
  forall x in BULK_COLLECTOR.first..BULK_COLLECTOR.last insert into ASYSTENT_TEST values BULK_COLLECTOR(x);
end;

So now when in table ASYSTENT_GOALS_SET column SUBSIDIARY (varchar) consists string 12_00_00 (which is code of one of subsidiary) everything works fine. But the problem is when user works in two subsidiaries, let say 12_00_00 and 13_00_00. I have no clue how to write it down. Should SUBSIDIARY column consist: '12_00_00','13_00_00' or "12_00_00","13_00_00" or maybe 12_00_00','13_00_00 I have tried a lot of options after digging on topics like "Deling with single/escaping/double qoutes". Maybe I should change something in execute immediate as well?

Or maybe my approach to that issue is completely wrong from the very beginning (hopefully not :) ). I would be grateful for support.

Brandon Minnick
  • 13,342
  • 15
  • 65
  • 123
  • If I understand correctly, the part that is not working is the `ppln_jsta_symbol in (:subsidiary)`? – Dessma Aug 24 '17 at 20:28
  • Yes Dessma, exactly. Apart from attempts in table ASYSTENT_GOALS_SET column SUBSIDIARY I mentioned above, I also tried to switch the code for ppln_jsta_symbol in :subsidiary and put ('12_00_00','13_00_00') as SUBSIDIARY. I also tried to use regexp_substr to read from my string in SUBSIDIARY column to make rows of it but with no success. – Maciej Kasprzak Aug 24 '17 at 22:16
  • 1
    I can't write you a working example because I don't have access to my database right now but I believe a large part of your answer is found here in the section with header "Using a TABLE function" : https://www.oratechinfo.co.uk/delimited_lists_to_collections.html#plsql_function Basically you can convert your string of elements into a TABLE and then use NOT IN (SELECT * FROM TABLE(f_convert(yourString))). Hope that makes sense. – Dessma Aug 25 '17 at 12:28

2 Answers2

0

I didn't create the table function described here but that article inspired me to go back to try regexp_substr function again.
I changed:
ppln_jsta_symbol in (:subsidiary)
to
ppln_jsta_symbol in (select regexp_substr((select subsidiary from ASYSTENT_GOALS_SET where user_id=''fake_num''),''[^,]+'', 1, level) from dual connect by regexp_substr((select subsidiary from ASYSTENT_GOALS_SET where user_id=''fake_num''), ''[^,]+'', 1, level) is not null)
Now it works like a charm! Thank you @Dessma very much for your time and suggestion!

0

"I wanted to avoid multiplying rows and thought it would be nice to put all conditions as a part of the code that I would use in 'where' clause further in procedure"

This seems a misguided requirement. You shouldn't worry about number of rows: databases are optimized for storing and retrieving rows.

What they are not good at is dealing with "multi-value" columns. As your own solution proves, it is not nice, it is very far from nice, in fact it is a total pain in the neck. From now on, every time anybody needs to work with subsidiary they will have to invoke a function. Adding, changing or removing a user's subsidiary is much harder than it ought to be. Also there is no chance of enforcing data integrity i.e. validating that a subsidiary is valid against a reference table.

Maybe none of this matters to you. But there are very good reasons why Codd mandated "no repeating groups" as a criterion of First Normal Form, the foundation step of building a sound data model.

The correct solution, industry best practice for almost forty years, would be to recognise that SUBSIDIARY exists at a different granularity to CHANNEL and so should be stored in a separate table.

APC
  • 144,005
  • 19
  • 170
  • 281
  • Thank you for your comment. I am still learning and as mentioned I had doubts it's not good approach. I do realize it is not the way data should be stored in tables. Normally I would prepare as many rows as necessary for user ID, to have unique entry of: USER ID, CHANNEL & SUBSIDIARY to make proper select of them later on.
    Long story short I was looking for some easy way for the person who will be responsible for changing subsidiaries of users.
    – Maciej Kasprzak Aug 26 '17 at 12:47