Search code examples
oracle-databaseplsqloracle12c

Column Names that need to be selected from a table comes from another table


I have two tables, table A contains the data in generic named columns. Which in other table B, I have a comma separated list of column names of table A.

Sample Data:

Table A:

+------+-----+-----+-----+-----+-----+------+-----+-----+-----+-----+-----+----+------+
| CODE | C1  | C2  | C3  | C4  | C5  |  C6  | C7  | M1  | M2  | M3  | M4  | M5 |  M6  |
+------+-----+-----+-----+-----+-----+------+-----+-----+-----+-----+-----+----+------+
| A1   | 102 | 275 | 400 | -55 |  -1 | 2779 |   5 | 157 | 236 | 332 | -94 |  7 | 2209 |
| A2   | 162 | 209 | 330 | -71 |  -4 | 1041 |   7 | 188 | 264 | 349 |  -3 | -8 | 3863 |
| A3   | 124 | 237 | 334 | -71 |   3 | 2086 |  24 | 126 | 299 | 366 | -36 | -8 | 1416 |
| A4   | 154 | 267 | 360 | -15 |   7 | 3900 |  24 | 103 | 260 | 316 | -92 |  2 | 3247 |
| A5   | 157 | 240 | 361 | -86 |   8 | 3871 | -13 | 123 | 296 | 380 | -80 | -6 | 3743 |
| A6   | 138 | 268 | 388 | -27 | -10 | 2939 | -23 | 181 | 229 | 393 | -46 | -6 | 1355 |
| A7   | 117 | 267 | 353 | -92 |  -6 | 3273 |  10 | 194 | 243 | 317 | -91 |  4 | 2508 |
| A8   | 138 | 207 | 343 | -67 |   3 | 1558 | -18 | 143 | 242 | 308 | -51 |  4 | 3955 |
| A9   | 175 | 225 | 359 | -11 |  -1 | 2344 |  13 | 174 | 229 | 380 |  -3 |  4 | 2611 |
| B1   | 137 | 295 | 364 | -48 |  -2 | 3827 |  24 | 169 | 257 | 304 | -58 |  9 | 1534 |
| B2   | 155 | 231 | 339 | -39 |  -1 | 2895 | -19 | 107 | 251 | 305 |  -1 |  2 | 2208 |
| B3   | 166 | 251 | 363 | -42 |  -2 | 1766 |   6 | 128 | 272 | 371 | -27 |  7 | 3460 |
| B4   | 116 | 200 | 317 | -72 |  -6 | 1073 | -13 | 160 | 230 | 307 | -73 | -8 | 3393 |
+------+-----+-----+-----+-----+-----+------+-----+-----+-----+-----+-----+----+------+

Table B:

+------+-------------------+
| CODE |    SELECT_COLS    |
+------+-------------------+
| A1   | C1,C2,C3,M1,M2,M3 |
| A2   | C1,C2,C3,M1,M2,M3 |
| A3   | C1,C2,C3,M1,M2,M3 |
| A4   | C1,C2,C3,C4,M1    |
| A5   | C1,C2,C3,C4,C5,M1 |
| A6   | C1,C2,C3,M1,M2    |
| A7   | C1,C2,C3,C4,M1,M2 |
| A8   | C1,C2,C3,C4,M1,M2 |
| A9   | C1,C2,C3,C4,M1,M2 |
| B1   | C1,C2,C3,C4,M1    |
| B2   | C1,C2,C3,C4,M1    |
| B3   | C1,C2,M1          |
| B4   | C1,M1             |
+------+-------------------+

Is there any way I can select only Columns specified in SELECT_COLS column of table B from table A?


Solution

  • Neither of the two potential solutions here require dynamic SQL or the use of PL/SQL and they will get you the data you require.

    Oracle does not support selecting from dynamic columns; yes, you could use PL/SQL to dynamically build an SQL statement but when that statement runs it will have fixed columns. Also, if you have one set of data that wants C1,C2,C3,M1,M2,M3 and another that wants C1,C2,C3,C4,C5,M1 then do you want 6 columns of data (plus the code) and, if so, how will you know what the columns contain? Or do you want 8 columns of data (6 from the first and the extra 2 from the second) and, if so, how do you know that you don't just want all the columns; so wouldn't it be easier just to get the data for every column and set the values to NULL that aren't meant to be shown and then you can hide columns which are all NULL in your application tier.

    So, instead of trying to get dynamic columns, select them as rows and then if you need to pivot them back to columns do it in whatever application tier you are using to access the database.

    SELECT a.*
    FROM   (
             SELECT Code, Key, Value
             FROM   tableA
             UNPIVOT (
               VALUE FOR KEY IN (
                 C1, C2, C3, C4, C5, C6, C7, M1, M2, M3, M3, M4, M5, M6
               )
             )
           ) a
           INNER JOIN (
             TableB
           ) B
           ON ( a.code = b.code AND INSTR( b.select_cols, a.key ) > 0 )
    

    Which, for your sample data:

    CREATE TABLE tableA ( CODE, C1, C2, C3, C4, C5, C6, C7, M1, M2, M3, M4, M5, M6 ) AS
    SELECT 'A1', 102, 275, 400, -55, -1, 2779, 5, 157, 236, 332, -94, 7, 2209 FROM DUAL UNION ALL
    SELECT 'A2', 162, 209, 330, -71, -4, 1041, 7, 188, 264, 349, -3, -8, 3863 FROM DUAL UNION ALL
    SELECT 'A3', 124, 237, 334, -71, 3, 2086, 24, 126, 299, 366, -36, -8, 1416 FROM DUAL UNION ALL
    SELECT 'A4', 154, 267, 360, -15, 7, 3900, 24, 103, 260, 316, -92, 2, 3247 FROM DUAL UNION ALL
    SELECT 'A5', 157, 240, 361, -86, 8, 3871, -13, 123, 296, 380, -80, -6, 3743 FROM DUAL UNION ALL
    SELECT 'A6', 138, 268, 388, -27, -10, 2939, -23, 181, 229, 393, -46, -6, 1355 FROM DUAL UNION ALL
    SELECT 'A7', 117, 267, 353, -92, -6, 3273, 10, 194, 243, 317, -91, 4, 2508 FROM DUAL UNION ALL
    SELECT 'A8', 138, 207, 343, -67, 3, 1558, -18, 143, 242, 308, -51, 4, 3955 FROM DUAL UNION ALL
    SELECT 'A9', 175, 225, 359, -11, -1, 2344, 13, 174, 229, 380, -3, 4, 2611 FROM DUAL UNION ALL
    SELECT 'B1', 137, 295, 364, -48, -2, 3827, 24, 169, 257, 304, -58, 9, 1534 FROM DUAL UNION ALL
    SELECT 'B2', 155, 231, 339, -39, -1, 2895, -19, 107, 251, 305, -1, 2, 2208 FROM DUAL UNION ALL
    SELECT 'B3', 166, 251, 363, -42, -2, 1766, 6, 128, 272, 371, -27, 7, 3460 FROM DUAL UNION ALL
    SELECT 'B4', 116, 200, 317, -72, -6, 1073, -13, 160, 230, 307, -73, -8, 3393 FROM DUAL;
    
    CREATE TABLE tableB ( CODE, SELECT_COLS ) AS
    SELECT 'A1', 'C1,C2,C3,M1,M2,M3' FROM DUAL UNION ALL
    SELECT 'A2', 'C1,C2,C3,M1,M2,M3' FROM DUAL UNION ALL
    SELECT 'A3', 'C1,C2,C3,M1,M2,M3' FROM DUAL UNION ALL
    SELECT 'A4', 'C1,C2,C3,C4,M1' FROM DUAL UNION ALL
    SELECT 'A5', 'C1,C2,C3,C4,C5,M1' FROM DUAL UNION ALL
    SELECT 'A6', 'C1,C2,C3,M1,M2' FROM DUAL UNION ALL
    SELECT 'A7', 'C1,C2,C3,C4,M1,M2' FROM DUAL UNION ALL
    SELECT 'A8', 'C1,C2,C3,C4,M1,M2' FROM DUAL UNION ALL
    SELECT 'A9', 'C1,C2,C3,C4,M1,M2' FROM DUAL UNION ALL
    SELECT 'B1', 'C1,C2,C3,C4,M1' FROM DUAL UNION ALL
    SELECT 'B2', 'C1,C2,C3,C4,M1' FROM DUAL UNION ALL
    SELECT 'B3', 'C1,C2,M1' FROM DUAL UNION ALL
    SELECT 'B4', 'C1,M1' FROM DUAL;
    

    Outputs:

    CODE | KEY | VALUE
    :--- | :-- | ----:
    A1   | C1  |   102
    A1   | C2  |   275
    A1   | C3  |   400
    A1   | M1  |   157
    A1   | M2  |   236
    A1   | M3  |   332
    A1   | M3  |   332
    A2   | C1  |   162
    A2   | C2  |   209
    A2   | C3  |   330
    A2   | M1  |   188
    A2   | M2  |   264
    A2   | M3  |   349
    A2   | M3  |   349
    A3   | C1  |   124
    A3   | C2  |   237
    A3   | C3  |   334
    A3   | M1  |   126
    A3   | M2  |   299
    A3   | M3  |   366
    A3   | M3  |   366
    A4   | C1  |   154
    A4   | C2  |   267
    A4   | C3  |   360
    A4   | C4  |   -15
    A4   | M1  |   103
    A5   | C1  |   157
    A5   | C2  |   240
    A5   | C3  |   361
    A5   | C4  |   -86
    A5   | C5  |     8
    A5   | M1  |   123
    A6   | C1  |   138
    A6   | C2  |   268
    A6   | C3  |   388
    A6   | M1  |   181
    A6   | M2  |   229
    A7   | C1  |   117
    A7   | C2  |   267
    A7   | C3  |   353
    A7   | C4  |   -92
    A7   | M1  |   194
    A7   | M2  |   243
    A8   | C1  |   138
    A8   | C2  |   207
    A8   | C3  |   343
    A8   | C4  |   -67
    A8   | M1  |   143
    A8   | M2  |   242
    A9   | C1  |   175
    A9   | C2  |   225
    A9   | C3  |   359
    A9   | C4  |   -11
    A9   | M1  |   174
    A9   | M2  |   229
    B1   | C1  |   137
    B1   | C2  |   295
    B1   | C3  |   364
    B1   | C4  |   -48
    B1   | M1  |   169
    B2   | C1  |   155
    B2   | C2  |   231
    B2   | C3  |   339
    B2   | C4  |   -39
    B2   | M1  |   107
    B3   | C1  |   166
    B3   | C2  |   251
    B3   | M1  |   128
    B4   | C1  |   116
    B4   | M1  |   160
    

    If you could have any of the rows and want them all as columns then just get them all and set the values to NULL if they aren't in the select_cols column:

    SELECT a.code,
           CASE WHEN INSTR( b.select_cols, 'C1' ) > 0 THEN C1 END AS C1,
           CASE WHEN INSTR( b.select_cols, 'C2' ) > 0 THEN C2 END AS C2,
           CASE WHEN INSTR( b.select_cols, 'C3' ) > 0 THEN C3 END AS C3,
           CASE WHEN INSTR( b.select_cols, 'C4' ) > 0 THEN C4 END AS C4,
           CASE WHEN INSTR( b.select_cols, 'C5' ) > 0 THEN C5 END AS C5,
           CASE WHEN INSTR( b.select_cols, 'C6' ) > 0 THEN C6 END AS C6,
           CASE WHEN INSTR( b.select_cols, 'C7' ) > 0 THEN C7 END AS C7,
           CASE WHEN INSTR( b.select_cols, 'M1' ) > 0 THEN M1 END AS M1,
           CASE WHEN INSTR( b.select_cols, 'M2' ) > 0 THEN M2 END AS M2,
           CASE WHEN INSTR( b.select_cols, 'M3' ) > 0 THEN M3 END AS M3,
           CASE WHEN INSTR( b.select_cols, 'M4' ) > 0 THEN M4 END AS M4,
           CASE WHEN INSTR( b.select_cols, 'M5' ) > 0 THEN M5 END AS M5,
           CASE WHEN INSTR( b.select_cols, 'M6' ) > 0 THEN M6 END AS M6
    FROM   tableA a
           INNER JOIN tableB b
           ON ( a.code = b.code )
    

    Which outputs:

    CODE |  C1 |   C2 |   C3 |   C4 |   C5 |   C6 |   C7 |  M1 |   M2 |   M3 |   M4 |   M5 |   M6
    :--- | --: | ---: | ---: | ---: | ---: | ---: | ---: | --: | ---: | ---: | ---: | ---: | ---:
    A1   | 102 |  275 |  400 | null | null | null | null | 157 |  236 |  332 | null | null | null
    A2   | 162 |  209 |  330 | null | null | null | null | 188 |  264 |  349 | null | null | null
    A3   | 124 |  237 |  334 | null | null | null | null | 126 |  299 |  366 | null | null | null
    A4   | 154 |  267 |  360 |  -15 | null | null | null | 103 | null | null | null | null | null
    A5   | 157 |  240 |  361 |  -86 |    8 | null | null | 123 | null | null | null | null | null
    A6   | 138 |  268 |  388 | null | null | null | null | 181 |  229 | null | null | null | null
    A7   | 117 |  267 |  353 |  -92 | null | null | null | 194 |  243 | null | null | null | null
    A8   | 138 |  207 |  343 |  -67 | null | null | null | 143 |  242 | null | null | null | null
    A9   | 175 |  225 |  359 |  -11 | null | null | null | 174 |  229 | null | null | null | null
    B1   | 137 |  295 |  364 |  -48 | null | null | null | 169 | null | null | null | null | null
    B2   | 155 |  231 |  339 |  -39 | null | null | null | 107 | null | null | null | null | null
    B3   | 166 |  251 | null | null | null | null | null | 128 | null | null | null | null | null
    B4   | 116 | null | null | null | null | null | null | 160 | null | null | null | null | null
    

    db<>fiddle here