How do I reuse a variable as a table name in select query - sql

I need to get an id from a table, whose table name is defined by running another query.
I tried to implement it by defining a variable that saves the results from the first query. Then I created a second query that consumes the table name from the first one.
define tablename = (SELECT table_name FROM tables_names WHERE row_id = 147);
define rowid = SELECT row_id FROM &tablename WHERE title is NULL;
select * from &rowid;
However it doesn't work. I also tried to create a stored procedure, but I don't understand how it should work either:
DECLARE
tablename varchar(128);
rowid int;
BEGIN
SELECT table_name INTO tablename FROM tables_names WHERE row_id = 147);
SELECT row_id INTO rowid FROM &tablename WHERE title is NULL;
END;
The output should give me the expected rowid. I'm not an Oracle expert and this seems like a simple thing, but I don't know how to achieve it.

Use execute immediate as following:
DECLARE
rowid_ int; -- dont use oracle reserved words as variable name.
-- added _ after rowid
BEGIN
EXECUTE IMMEDIATE 'SELECT row_id FROM '
|| (SELECT table_name FROM tables_names WHERE row_id = 147)
|| ' WHERE title is NULL' INTO ROWID_;
-- do something with rowid_ or other logic
END;
/
Cheers!!

This is called dynamic sql.
Edit the procedure and have it do
EXECUTE IMMEDIATE
"SELECT row_id INTO :x FROM " || tablename || " WHERE title IS NULL"
USING rowid;
This is from memory so it might not work, but should give you a good start on how to do it

Related

Drop multiple tables in the same DB -starts with same prefix - Big Query

How can I drop multiple tables in the same database that starts with same prefix?
Ex:
Query to delete 1 table
drop table project_id.db.test_table_<some_random_string>
But how can I drop all tables that start with the same prefix test_table_ in the same db?
A possible work around would be. (region is set eu)
BEGIN
DECLARE drop_statments ARRAY<string>;
DECLARE len int64 default 1;
SET drop_statments = (SELECT ARRAY_AGG( 'drop table ' || table_schema ||'.' || table_name)
FROM `region-eu.INFORMATION_SCHEMA.TABLES`
WHERE table_schema = 'db' and table_name like 'Table_Prefix%'
);
WHILE ARRAY_LENGTH(drop_statments) >= len DO
EXECUTE IMMEDIATE drop_statments[offset(len-1)];
SET len = len +1;
END WHILE ;
END;
You may use any of below INFORMATION_SCHEMA dataset.
-- Returns metadata for tables in a single dataset.
SELECT * FROM myDataset.INFORMATION_SCHEMA.TABLES;
-- Returns metadata for tables in a region.
SELECT * FROM region-us.INFORMATION_SCHEMA.TABLES;
Similar to narendra# solution, but using FOR..IN:
FOR drop_statement IN
(SELECT CONCAT("drop table ",table_schema,".", table_name, ";" ) AS value
FROM dataset.INFORMATION_SCHEMA.TABLES -- or region.INFORMATION_SCHEMA.TABLES
WHERE table_name LIKE "table_prefix%"
ORDER BY table_name DESC)
DO
EXECUTE IMMEDIATE(drop_statement.value); -- Here the table is dropped
END FOR;
It's also worth to mention, that you can change "table" to "view" also depending on the information returned from the INFORMATION_SCHEMA views.

Select from table name defined in a loop

I have 2 database connections: a and b where they both share the same schema but not necessarily the same data. I need to check both database's total number of rows for each table using a loop and compare their counts.
Using a nested table collection, my attempt so far:
DECLARE
TYPE nt_rows IS TABLE OF NUMBER;
nt_rows1 nt_num := nt_num();
nt_rows2 nt_num := nt_num();
counter INT := 0;
CURSOR c_tables1 IS
SELECT TABLE_NAME
FROM ALL_TABLES
WHERE OWNER IN ('a')
ORDER BY TABLE_NAME ASC;
CURSOR c_tables2 IS
SELECT TABLE_NAME
FROM ALL_TABLES
WHERE OWNER IN ('b')
ORDER BY TABLE_NAME ASC;
BEGIN block: loop through each cursor and add them to each array
BEGIN
FOR i IN c_tables1 LOOP
nt_rows1.extend;
SELECT COUNT(*) FROM a.i.TABLE_NAME INTO nt_rows1(counter);
END LOOP;
The last line does not work where 'a' is the database connection name and i is index of each table_name.
I also tried:
nt_rows1(counter) := SELECT COUNT(*) FROM a.i.TABLE_NAME;
and it also doesn't work, any suggestions are appreciated.
If you want to query data from a table whose name is in a string (for example a local variable or a cursor field) and hence not known at compile time, you will need to use dynamic SQL.
Try replacing the line
SELECT COUNT(*) FROM a.i.TABLE_NAME INTO nt_rows1(counter);
with
EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM a.' || i.TABLE_NAME INTO nt_rows1(counter);
Note also that your code is not incrementing the counter variable. Since you are starting with counter set to 0, and 0 isn't a valid index into a nested table, I would recommend adding a line to increment counter before the line above.

Find tables, columns with specific value

I'm using Firebird 2.5.0. I know a value and need to find all tables, columns in which it occurs.
I created procedure:
CREATE OR ALTER PROCEDURE NEW_PROCEDURE (
searching_value varchar(30))
returns (
table_with_value varchar(100),
column_with_value varchar(100))
as
declare variable all_tables varchar(50);
declare variable all_columns varchar(50);
declare variable all_values varchar(50);
begin
FOR SELECT
r.rdb$relation_name, f.rdb$field_name
from rdb$relation_fields f
join rdb$relations r on f.rdb$relation_name = r.rdb$relation_name
and r.rdb$view_blr is null
and (r.rdb$system_flag is null or r.rdb$system_flag = 0)
order by 1, f.rdb$field_position INTO :all_tables, :all_columns
DO
BEGIN
FOR SELECT all_columns FROM all_tables
INTO :all_Values
DO
BEGIN
IF (SEARCHING_VALUE = all_Values) THEN
BEGIN
table_With_Value = all_Tables;
column_With_Value = all_Columns;
SUSPEND;
END
END
END
END^
When I run it I get error message:
Undefined name.
Dynamic SQL Error.
SQL error code = -204.
Table unknown.
ALL_TABLES.
At line 21, column 13.
So in this select statement "SELECT all_columns FROM all_tables" it is not taking values from previous for select statement but just trying to find table all_tables. How to fix it?
The problem is that all_columns is considered to be a colum name and all_tables a table name and not your variables in:
SELECT all_columns FROM all_tables
You can't parametrize objectnames in a query like this. Also note that if it had been possible to parametrize object names, you would have had to use :all_columns and :all_tables for disambiguation.
Instead you will need to create a dynamic SQL statement and execute that with EXECUTE STATEMENT (or more specifically: FOR EXECUTE STATEMENT).
In this case:
FOR EXECUTE STATEMENT 'SELECT "' || all_columns || '" FROM "' || all_tables || '"'
INTO :all_values
DO
BEGIN
/* .... */
END
I have quoted the object names to account for case sensitive column and table names (or identifiers that are invalid unquoted). Constructing a query like this might leave you open to SQL injection if the values are obtained from another source than the Firebird metadata tables.

How to choose tables on select from all_tables?

I have the following table name template, there are a couple with the same name and a number at the end: fmj.backup_semaforo_geo_THENUMBER, for example:
select * from fmj.backup_semaforo_geo_06391442
select * from fmj.backup_semaforo_geo_06398164
...
Lets say I need to select a column from every table which succeeds with the 'fmj.backup_semaforo_geo_%' filter, I tried this:
SELECT calle --This column is from the backup_semaforo_geo_# tables
FROM (SELECT table_name
FROM all_tables
WHERE owner = 'FMJ' AND table_name LIKE 'BACKUP_SEMAFORO_GEO_%');
But I'm getting the all_tables tables name data:
TABLE_NAME
----------
BACKUP_SEMAFORO_GEO_06391442
BACKUP_SEMAFORO_GEO_06398164
...
How can I achieve that without getting the all_tables output?
Thanks.
Presumably your current query is getting ORA-00904: "CALLE": invalid identifier, because the subquery doesn't have a column called CALLE. You can't provide a table name to a query at runtime like that, unfortunately, and have to resort to dynamic SQL.
Something like this will loop through all the tables and for each one will get all the values of CALLE from each one, which you can then loop through. I've used DBMS_OUTPUT to display them, assuming you're doing this in SQL*Plus or something that can deal with that; but you may want to do something else with them.
set serveroutput on
declare
-- declare a local collection type we can use for bulk collect; use any table
-- that has the column, or if there isn't a stable one use the actual data
-- type, varchar2(30) or whatever is appropriate
type t_values is table of table.calle%type;
-- declare an instance of that type
l_values t_values;
-- declare a cursor to generate the dynamic SQL; where this is done is a
-- matter of taste (can use 'open x for select ...', then fetch, etc.)
-- If you run the query on its own you'll see the individual selects from
-- all the tables
cursor c1 is
select table_name,
'select calle from ' || owner ||'.'|| table_name as query
from all_tables
where owner = 'FMJ'
and table_name like 'BACKUP_SEMAFORO_GEO%'
order by table_name;
begin
-- loop around all the dynamic queries from the cursor
for r1 in c1 loop
-- for each one, execute it as dynamic SQL, with a bulk collect into
-- the collection type created above
execute immediate r1.query bulk collect into l_values;
-- loop around all the elements in the collection, and print each one
for i in 1..l_values.count loop
dbms_output.put_line(r1.table_name ||': ' || l_values(i));
end loop;
end loop;
end;
/
May be a dynamic SQL in a PLSQL program;
for a in (SELECT table_name
FROM all_tables
WHERE owner = 'FMJ' AND table_name LIKE 'BACKUP_SEMAFORO_GEO_%')
LOOP
sql_stmt := ' SELECT calle FROM' || a.table_name;
EXECUTE IMMEDIATE sql_stmt;
...
...
END LOOP;

Oracle/SQL - Using query results as paramaters in another query -looping?

Hi everyone what I'm wondering if I can do is create a table that lists the record counts of other tables. It would get those table names from a table. So let's assume I have the table TABLE_LIST that looks like this
name
---------
sports_products <-- contains 10 records
house_products <-- contains 8 records
beauty_products <-- contains 15 records
I would like to write a statement that pulls the names from those tables to query them and coount the records and ultimately produce this table
name numRecords
------------------------------
sports_products 10
house_products 8
beauty_products 15
So I think I would need to do something like this pseudo code
select *
from foreach tableName in select name from table_list
select count(*) as numRecords
from tableName
loop
You can have a function that is doing this for you via dynamic sql.
However, make sure to declare it as authid current_user. You do not want anyone to gain some sort of privilege elevation by exploiting your function.
create or replace function SampleFunction
(
owner in VarChar
,tableName in VarChar
) return integer authid current_user is
result Integer;
begin
execute immediate 'select count(*) from "' || owner || '"."' || tableName || '"'
INTO result;
return result;
end;
One option is to simply keep your DB statistics updated, use dbms_stats package or EM, and then
select num_rows
from all_tables
where table_name in (select name from table_list);
I think Robert Giesecke solution will work fine.
A more exotic way of solving this is by using dbms_xmlgen.getxml.
See for example: Identify a table with maximum rows in Oracle