oracle query select a column (where column name is unknown) - oracle

My column name is retrieved from another query, then using that result as the column name I want to query to get the value. I have found a duplicate question here but I could not find the answer. So my code looks like this:
DECLARE
COL_NO NUMBER(3,0);
COL_NAME VARCHAR2(30);
COL_VALUE VARCHAR2(100);
BEGIN
COL_NO:=0;
COL_NAME:=NULL;
COL_VALUE:=NULL;
SELECT COUNT(*) INTO COL_NO FROM user_tab_columns WHERE table_name='SALARYSLIP';
FOR A IN 4..COL_NO LOOP
SELECT column_name INTO COL_NAME FROM user_tab_columns WHERE column_id=A AND table_name='SALARYSLIP';
--SELECT COL_NAME INTO COL_VALUE FROM SALARYSLIP WHERE ID=SALARYSLIP.ID;
DBMS_OUTPUT.PUT_LINE(COL_VALUE);
END LOOP;
END;
The commented query is supposed to fetch the column value but is not working obviously. What should I write there? TIA.

I'm not exactly sure what you are trying to accomplish. Counting the umber of columns in the table and iterating from 4 to that number doesn't seem particularly logical. In the desired SQL statement, it's also not clear where you are getting the value for the id that you want to use.
If you want to determine the column name at runtime, then you would need to use dynamic SQL. Something like
FOR A IN 4..COL_NO LOOP
SELECT column_name INTO COL_NAME FROM user_tab_columns WHERE column_id=A AND table_name='SALARYSLIP';
--SELECT COL_NAME INTO COL_VALUE FROM SALARYSLIP WHERE ID=SALARYSLIP.ID;
EXECUTE IMMEDIATE 'SELECT ' || col_name ||
' FROM salaryslip ' ||
' WHERE id = :1'
INTO col_value
USING <<whatever ID value you want>>;
DBMS_OUTPUT.PUT_LINE(COL_VALUE);
END LOOP;
For simplicity, I'm not using a local variable for the SQL statement that is assembled before being dynamically executed. Normally, you would want to have a local variable with the SQL statement so that you can do things like log the statement that was assembled in case it doesn't do what you expected.

I think the logic needs to be addressed first - i.e. whether the required results is in terms of the rows from the SALARYSLIP table, OR the columns of the SALARYSLIP table.
SELECT COUNT(*) INTO COL_NO FROM user_tab_columns WHERE table_name='SALARYSLIP'; ( this would get the number of columns from the SALARYSLIP table )
After this, what is the desired result to iterate through the for loop, based on the number of columns on the SALARYSLIP table? i.e. since, generally, the output is based on the rows being retrieved.
Where is the "ID" value coming from? It's not being initialized / declared in the code.
From what I gather, the column_name of the table is required to be retrieved at runtime. Then, the data for this column should be displayed for the SALARYSLIP table.
Try this :
DECLARE
COL_NO NUMBER(3,0);
COL_NAME VARCHAR2(30);
COL_VALUE VARCHAR2(100);
ROWNO NUMBER(3,0);
BEGIN
COL_NO:=0;
COL_NAME:=NULL;
COL_VALUE:=NULL;
SELECT COUNT(*) INTO COL_NO FROM user_tab_columns WHERE table_name='SALARYSLIP'; --number of columns in SALARYSLIP
SELECT Count(*) INTO rownno FROM SALARYSLIP; --number of rows in SALARYSLIP
--go through each column
FOR A IN 1..COL_NO LOOP
SELECT column_name INTO COL_NAME FROM user_tab_columns WHERE column_id=A AND table_name='SALARYSLIP'; --column_name
--go through each row in the SALARYSLIP table
--for each row, get the specific column of the row, and print this column's value
FOR B IN 1..rowno LOOP
SELECT COL_NAME INTO COL_VALUE FROM SALARYSLIP WHERE =SALARYSLIP.COL_NAME = COL_NAME;
DBMS_OUTPUT.PUT_LINE(COL_VALUE);
END LOOP;
END LOOP;
END;

Related

Why does this PL/SQL not find a table?

I have 2 table DOQUANGDUC1 and DOQUANGDUC, in table DOQUANGDUC list table in database I need check:
declare
BEGIN
FOR x IN (select table_name,column_name from DOQUANGDUC) LOOP
INSERT INTO DOQUANGDUC1 (table_name,column_name)
SELECT count(x.column_name),column_name FROM x.table_name GROUP BY column_name having count(x.column_name) >2;
END LOOP;
END;
I run sql error
Error at line 1
ORA-06550: line 9, column 57:
PL/SQL: ORA-00942: table or view does not exist
ORA-06550: line 8, column 10:
PL/SQL: SQL Statement ignored
I am sure two table already use.
PL/SQL: ORA-00942: table or view does not exist
The cause of the error message is this line:
FROM x.table_name
This is telling Oracle to query a table called table_name in a schema called X and that does not exist. Hence the failure.
I think what you're trying to do is loop through a list of tables and columns and count how many values each column has, and insert the results into another table. You can't do that with pure SQL, you need to use dynamic SQL to format the statement.
declare
stmt varchar2(32767);
begin
for x in (select table_name, column_name from doquangduc) loop
stmt := 'insert into doquangduc1(table_name, column_name, col_count) '
||' select '''||x.table_name||''', '''||x.column_name||''', count('||x.column_name||') '
||' from '||x.table_name
||' having count('||x.column_name||') > 2';
execute immediate stmt;
end loop;
end;
/
Here is a working demo on db<>fiddle.
Note that I have changed the insert statement to populate three columns in doquangduc1: correct this to fit your actual table structure (which you haven't posted).
It seems that the CMS_CIF_MUREX_NAME table has 292 records I add 1 duplicate record when running your command it shows the result column col_count = 293
My interpretation of your requirement is to count how many rows are populated for each column. Maybe that is not your actual requirement. Such misunderstandings are always a risk when you post a piece of code and expect us to reverse engineer your business logic. It is easier if you tell us what you're actually trying to achieve.
If you want counts for each distinct value in the column you can add a GROUP clause before the HAVING clause in my query.
'group by '||x.column_name ||
You may also want to add the value to the records inserted into doquangduc1, so you know which value has which count.
Approaches to Inserting into a Table through an Implicit Cursor or a Select Statement
Our Requirement:
We are going through the records in doquangduc and finding instances where the column_name appears more than twice.
Next, we are placing these records in another table, doquangduc1, while preserving the counts (placing them in doquangduc1).
Ultimately, these records are needed so they can be viewed.
Comments about this requirement:
It is not really clear that we really need to perform a transformation of the table, doquangduc, and then insert into another table to view the resulting records. This answer assumes this is necessary.
We create and execute DDL for tables needed first.
CREATE TABLE doquangduc
AS
SELECT
table_name,
column_name
FROM
user_tab_columns;
We create the table, doquangduc1, and make sure there is a column called col_count:
CREATE TABLE doquangduc1
AS
SELECT
table_name,
column_name,
1 col_count
FROM
user_tab_columns
WHERE
table_name IS NULL;
We sort through the doquangduc table to identify scenarios you have identified.
BEGIN
FOR x IN (
WITH qry AS (
SELECT
table_name,
column_name,
COUNT(1)
OVER(PARTITION BY column_name) AS col_count
FROM
user_tab_columns
)
SELECT
table_name,
column_name,
col_count
FROM
qry
WHERE
col_count > 2
) LOOP
INSERT INTO doquangduc1 (
table_name,
column_name,
col_count
) VALUES (
x.table_name,
x.column_name,
x.col_count
);
END LOOP;
END;
As various people have commented, do we really need an implicit cursor to perform this insert into the table, doquangduc1, so that we can view the results?
We could just perform a INSERT INTO with a SELECT statement to more efficiently insert these records into doquangduc1:
INSERT INTO doquangduc1
WITH qry AS (
SELECT
table_name,
column_name,
COUNT(1)
OVER(PARTITION BY column_name) AS col_count
FROM
user_tab_columns
)
SELECT
table_name,
column_name,
col_count
FROM
qry
WHERE
col_count > 2;

Select Query Column name based on Table name

I have like thousands tables, each of them will have their own unique key(s) columns. I want to select the keys based on the table name in a single query. Its always the second column in the table if it helps.
I want something like
select c_name from t_name
where c_name (is)
(
select column_name as c_name
from Dba_tab_Columns
where table_name = t_name
and column_name like '%name' --->>>(((or column_id =2)))
)
I know the t_name but I need a single query to select column name based on table_name.
So let say if I say select c_name from Animals, it should give me list of all animals and if I say select c_name from Cars, it should give me a list of avilable cars.
You cannot do this in pure SQL, you'll need a table function. Here's a way:
create or replace type tvc as table of varchar2(128);
/
create or replace function return_col (tname user_tables.table_name%type) return tvc pipelined
as
c_statement varchar2(400);
get_data sys_refcursor;
out_d varchar2(128);
begin
for gettnames_and_cols in (select c.column_name
from user_cons_columns c, user_constraints uc
where constraint_type in ('P','U') and uc.table_name=c.table_name and c.table_name=upper(tname)) loop
c_statement:='select '||gettnames_and_cols.column_name||' as output_col from '||tname;
open get_data for c_statement;
while true loop
fetch get_data into out_d;
exit when get_data%notfound;
pipe row(out_d);
end loop;
close get_data;
end loop;
return;
end;
/
Thing is that this just gives the data, with no idea what's the column_name or from which table the data comes. You can modify the PL/SQL code to add this info. Also, this assumes that the data will be returned as VARCHAR2(128), you may need to adapt this to your needs. You use this table function as follows:
select *
from table (return_col('your_table_name'));

Data not inserting to destination table

I have the following block of PL-SQL code in Oracle:
DECLARE TAB VARCHAR(100);
COL VARCHAR(100);
CURSOR C_COLS IS
select DISTINCT table_name, column_name
from all_tab_columns
where OWNER = 'MyDB' AND DATA_TYPE LIKE '%VARCHAR%';
BEGIN
OPEN C_COLS;
LOOP
FETCH C_COLS INTO TAB, COL;
EXIT WHEN C_COLS%notfound;
INSERT INTO TargetTable (TABLE_NAME, COLUMN_NAME, COLUMN_VALUE)
SELECT DISTINCT TAB,
COL,
(SELECT COL FROM TAB)
FROM TAB
WHERE REGEXP_LIKE(COL, '([ABCDEFGHIJKLMNOPQRSTUVWXYZ])\d\d\d\d\d\d([ABCDEFGHIJKLMNOPQRSTUVWXYZ])', 'ix');
END LOOP;
CLOSE C_COLS;
END;
The idea is to determine which tables in my rather large database contain a certain pattern of data and to find them.
So I want to return three columns: TableName, ColumnName, Value of ColumnName.
The above runs but returns no data and I can't understand why. The query in the cursor returns results, and if I hard code the table values into a simple select statement containing my Regex, I get results. I just want one result set that contains the thousands of results I expect.
Could it be the (SELECT COL FROM TAB) I'm using to dynamically find the column_value? I wasn't sure if I could express it this way.
If you want to select columns dynamically you may wish to try dynamic SQL.
DECLARE
w_sql VARCHAR2(32767);
BEGIN
DBMS_OUTPUT.enable(32767);
FOR s_cols IN (
select DISTINCT
table_name
, column_name
from all_tab_columns
where owner = 'MyDB'
AND data_type LIKE '%VARCHAR%'
)
LOOP
w_sql := q'!
INSERT
INTO TargetTable (TABLE_NAME, COLUMN_NAME, COLUMN_VALUE)
SELECT DISTINCT
':TAB'
, ':COL'
, :COL
FROM :TAB
WHERE REGEXP_LIKE(:COL, '([ABCDEFGHIJKLMNOPQRSTUVWXYZ])\d\d\d\d\d\d([ABCDEFGHIJKLMNOPQRSTUVWXYZ])', 'ix')
!';
w_sql := REPLACE(w_sql, ':TAB', s_cols.table_name);
w_sql := REPLACE(w_sql, ':COL', s_cols.column_name);
EXECUTE IMMEDIATE w_sql;
END LOOP;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.put_line('Error for SQL :'|| w_sql ||'; error is :'|| SQLERRM);
END;

Setting the size of a varray to the result of a count SQL statement

I'm trying to store the result of a fairly simple select statement in a varray
select id from my_table where column1 is not null;
Ideally, I'd declare my varray and set its size to be
select count(id) from my_table where column1 is not null;
something like this pseudo-code:
type my_array_type is varray(select count(id) from my_table where column1 is not null) of int;
my_array my_array_type := my_array_type();
my_array.extend(select count(id) from my_table where column is not null);
FOR i IN 1..my_array.count LOOP
my_array(i) := <get id from select statement using cursor>;
END LOOP;
Is there a better approach to what I'm trying to achieve?
Dynamic size initialization with varray is not possible,you can try Nested table instead of it.
(By reference of Declare dynamic array in Oracle PL/SQL)
Please check sample code mentioned below.
DECLARE
type my_array_type is TABLE of MY_TABLE.COLUMN1%TYPE;
l_employees my_array_type;
BEGIN
-- All rows at once...
SELECT COLUMN1
BULK COLLECT INTO l_employees
FROM MY_TABLE
ORDER BY COLUMN1;
DBMS_OUTPUT.put_line (l_employees.COUNT);
FOR indx IN 1 .. l_employees.COUNT
LOOP
DBMS_OUTPUT.put_line (l_employees(indx));
END LOOP;
END;
Hope this will help you!

For Loop in Oracle / Toad

I am new to Oracle and PL/SQL and am trying to do the following.
I am returning the column names from a table name that is stored in a variable
variable v_table varchar2(100)
begin
select 'mytable' into :v_table from dual;
end;
select column_name from all_tab_columns where table_name = :v_table
This returns a rowset
column_name
colname1
colname2
colname3
I would like to loop through the returned rowset and get some stats for each column.
select count distinct(colname1), min(colname1), max(colname1)
from :v_table
group by min(colname1), max(colname1)
However I cannot figure out how to loop through this rowset.
By using below you can display column names of a table and append your sql query as per your requirement...
declare
v_table varchar2(100):='TABLE_NAME';
TYPE NT_VAR1 IS TABLE OF VARCHAR2(30);
TYP_NT NT_VAR1;
begin
select column_name BULK COLLECT INTO TYP_NT from all_tab_columns where table_name =v_table
order by column_id;
FOR I IN 1..TYP_NT.COUNT
LOOP
DBMS_OUTPUT.PUT_LINE('COLUMN '||I||': '||TYP_NT(I));
END LOOP;
end;
Note: You should run select query dynamically means with the use of execute immediate why because you are passing table_name at run time.
Edit
After editing your query in for loop it will look like below.
declare
v_table varchar2(100):='TABLE_NAME';
TYPE NT_VAR1 IS TABLE OF VARCHAR2(30);
TYP_NT NT_VAR1;
var2 varchar2(2000);
var3 varchar2(2000);
begin
select column_name BULK COLLECT INTO TYP_NT from all_tab_columns where table_name =v_table
order by column_id;
FOR I IN 1..TYP_NT.COUNT
LOOP
DBMS_OUTPUT.PUT_LINE('COLUMN '||I||': '||TYP_NT(I));
EXECUTE IMMEDIATE 'SELECT to_char(min('||TYP_NT(I)||')),to_char(max('||TYP_NT(I)||')) from '||v_table into var2,var3;
dbms_output.put_line(var2||' '||var3);
END LOOP;
end;
Note: Find the max data length of a column present in your table and assign same data type to var2 and var3 variables.

Resources