Select all record from all the tables, every derived table must have its own alias - sql

I'm working on a e-learning project in which there is a table named chapter in which there is a column named question_table this is table in which the specific chapter's questions are added.
Now the problem is I want to display all the question from all the chapter for this I used following sql query
SELECT * FROM (SELECT `question_table` FROM `chapter`)
but it doesn't work and gives the error:
"Every derived table must have its own alias".
Note: I want to do it using SQL not PHP.

Firstly, I think you would be better redesigning your database. Multiple tables of the same structure holding the same data are generally not a good idea.
However what you require is possible using a MySQL procedure to build up some dynamic SQL and then execute it, returning the resulting data.
A procedure as follows could be used to do this:-
DROP PROCEDURE IF EXISTS dynamic;
delimiter //
CREATE PROCEDURE dynamic()
BEGIN
DECLARE question_table_value VARCHAR(25);
DECLARE b INT DEFAULT 0;
DECLARE c TEXT DEFAULT '';
DECLARE cur1 CURSOR FOR SELECT `question_table` FROM `chapter`;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET b = 1;
OPEN cur1;
SET b = 0;
WHILE b = 0 DO
FETCH cur1 INTO question_table_value;
IF b = 0 THEN
IF c = '' THEN
SET c = CONCAT('SELECT * FROM `',question_table_value, '`');
ELSE
SET c = CONCAT(c, ' UNION SELECT * FROM `',question_table_value, '`');
END IF;
END IF;
END WHILE;
CLOSE cur1;
SET #stmt1 := c;
PREPARE stmt FROM #stmt1;
EXECUTE stmt;
END
This is creating a procedure called dynamic. This takes no parameters. It sets up a cursor to read the question_table column values from the chapter table. It looks around the results from that, building up a string which contains the SQL, which is a SELECT from each table with the results UNIONed together. This is then PREPAREd and executed. The procedure will return the result set from the SQL executed by default.
You can call this to return the results using:-
CALL dynamic()
Down side is that this isn't going to give nice results if there are no rows to return and they are not that easy to maintain or debug with the normal tools developers have. Added to which very few people have any real stored procedure skills to maintain it in future.

In MySQL you must give every subquery ("derived table") an alias:
SELECT * FROM (SELECT question_table FROM chapter) t --notice the alias "t"

The derived table here is the result of the (SELECT ...). You need to give it an alias, like so:
SELECT * FROM (SELECT question_table FROM chapter) X;
Edit, re dynamic tables
If you know all the tables in advance, you can union them, i.e.:
SELECT * FROM
(
SELECT Col1, Col2, ...
FROM Chapter1
UNION
SELECT Col1, Col2, ...
FROM Chapter2
UNION
...
) X;
SqlFiddle here
To do this solution generically, you'll need to use dynamic sql to achieve your goal.
In general however, this is indicative of a smell in your table design - your chapter data should really be in one table, and e.g. classified by the chapter id.
If you do need to shard data for scale or performance reasons, the typical mechanism for doing this is to span multiple databases, not tables in the same database. MySql can handle large numbers of rows per table, and performance won't be an issue if the table is indexed appropriately.

Related

UNION with a stored procedure

This is what I am trying to do
EXEC sp1 1
SELECT * FROM x
UNION
if(#num <= 1)
EXEC sp1(2)
else
null //want to return null to stop
I could do this is with a programming language but I don't have an idea what is that I am doing wrong with programming in SQL?
This, honestly, makes no sense, and I still suggest that you use an inline Table-value function here, instead of a procedure, but you can do something like this using OPENROWSET to return the dataset from a stored procedure within a SELECT statement. It can't be parametrised though (not in the traditional sense), and if you don't understand this, don't use it.
This is pseudo SQL as well as there's a lack of enough information to provide a complete solution, such as the columns needed in the SELECTs, but it might get you there if you can comprehend it:
EXEC dbo.sp1 1;
SELECT {Columns}
FROM dbo.x
UNION ALL
SELECT {Same Columns again} --This dataset's definition must be IDENTICAL to the above against your table dbo.x
FROM OPENROWSET('SQLNCLI', 'Server=localhost;Trusted_Connection=Yes;Database={YourDatabase}','EXEC dbo.sp1(2);') ORS; --Assumes you are using Windows Authentication
WHERE #Num <= 1
UNION ALL
SELECT NULL,NULL,NULL{,NULL...} --Until you have you enough NULL columns
Note that in this example I am using the deprecated SQLNCLI connection manager. You should really be using MSOLEDBSQL, however, the only instance I currently have access to with a trusted connection is a 2012 instance which doesn't have that driver installed; so I didn't want to post code that I hadn't minimally tested.
You can achieve this in SQL this way:
SELECT * into #temp FROM x
if(#num <= 1)
begin
insert into #temp
EXEC sp1(2)
select * from #temp
end
else
begin
select null
end
First you create a temp table and insert x table records into it and after that you check your condition and then insert records from procedure and then select * from #temp and other case it will return null.

Is it possible to pass variable tables through procedures in SQL DEV?

set serveroutput on;
CREATE OR REPLACE PROCEDURE test_migrate
(
--v_into_table dba_tables.schema#dbprd%TYPE,
--v_from_table dba_tables.table#dbprd%TYPE,
v_gid IN NUMBER
)
IS
BEGIN
select * INTO fx.T_RX_TXN_PLAN
FROM fx.T_RX_TXN_PLAN#dbprd
WHERE gid = v_gid;
--and schema = v_into_table
--and table = v_from_table;
COMMIT;
END;
I thought that SELECT * INTO would create a table in the new database from #dbprd. However, the primary issue is just being able to set these as variables and the goal is to EXEC(INTO_Table,FROM_Table,V_GID) to run the above code.
Error(9,19): PLS-00201: identifier 'fx.T_RX_TXN_PLAN' must be
declared  Error(10,5): PL/SQL: ORA-00904: : invalid identifier
If your goal is to copy data from table in "another" database into a table that resides in "this" database (regarding database link you used), then it it INSERT INTO, not SELECT INTO.
For example:
CREATE OR REPLACE PROCEDURE test_migrate (v_gid in number)
IS
BEGIN
insert into fx.t_rx_txn_plan (col1, col2, ..., coln)
select col1, col2, ..., coln
from fx.t_rx_txn_plan#dbprod
where gid = v_gid;
END;
Last sentence you wrote looks like you'd want to make it dynamic, i.e. pass table names and v_gid (whatever that might be; looks like all tables that should be involved into this process have it). That isn't a simple task.
If you plan to use insert into select * from, that's OK but not for production system. What if someone alters a table and adds (or drops) a column or two? Your procedure will automatically fail. Correct way to do it is to enumerate all columns involved, but that requires fetching data from user_tab_columns (or all_ or dba_ version of the same), which complicates it even more.
Therefore, if you want to move data from here to there, why don't you do it using Data Pump Export & Import? Those utilities are designed for such a purpose, and will do the job better than your procedure. At least, I think so.
This way you should be returning a row. If so, add an OUT type parameter to the procedure with
CREATE OR REPLACE PROCEDURE test_migrate(
--v_into_table dba_tables.schema#dbprd%TYPE,
--v_from_table dba_tables.table#dbprd%TYPE,
i_gid IN NUMBER,
o_RX_TXN_PLAN OUT fx.T_RX_TXN_PLAN#dbprd%rowtype
) IS
BEGIN
SELECT *
INTO RT_RX_TXN_PLAN
FROM fx.T_RX_TXN_PLAN#dbprd
WHERE id = v_gid;
--and schema = v_into_table
--and table = v_from_table;
END;
and call the procedure such as
declare
v_rx_txn_plan fx.T_RX_TXN_PLAN#dbprd%rowtype;
v_gid number:=5345;
begin
test_migrate(v_gid => v_gid, rt_rx_txn_plan => v_rx_txn_plan);
dbms_output.put_line(v_rx_txn_plan.col1);
dbms_output.put_line(v_rx_txn_plan.col2);
end;
to print out the returning values for some columns of the table. to be able to create a new table from this, not SELECT * INTO ... syntax, but
CREATE TABLE T_RX_TXN_PLAN AS
SELECT *
INTO RT_RX_TXN_PLAN
FROM fx.T_RX_TXN_PLAN#dbprd
WHERE ...
is used.
But neither of the cases to issue a COMMIT since there's no DML exists within them.
To create a table you must use the CREATE TABLE statement, and to use any DDL statement in PL/SQL you have to use EXECUTE IMMEDIATE:
CREATE OR REPLACE PROCEDURE test_migrate
(
v_gid IN NUMBER
)
IS
BEGIN
EXECUTE IMMEDIATE 'CREATE TABLE FX.T_RX_TXN_PLAN AS
SELECT *
FROM fx.T_RX_TXN_PLAN#dbprd
WHERE gid = :GID'
USING IN v_gid;
END;

Db2 for i - dynamic FROM clause

Environment: Db2 for i, version 7.3
Library/table structure:
CORPORATE/TENANTS
LIB01/INVOICE
LIB02/INVOICE
LIB03/INVOICE
…
LIBxx/INVOICE
The CORPORATE/TENANTS table contains a list of libraries where information about each tenant is stored. It has this structure and data:
CREATE OR REPLACE TABLE TENANTS (
ID BIGINT GENERATED ALWAYS AS IDENTITY (START WITH 1),
TENANT CHAR(10) NOT NULL,
PRIMARY KEY(ID)
) RCDFMT TENANTSR;
RUNSQLSTM SRCFILE(HILLB/QDDLSRC) SRCMBR(TENANTS) DFTRDBCOL(CORPORATE)
+--+------+
|ID|TENANT|
+--+------+
| 1|LIB01 |
| 2|LIB02 |
|..|......|
|99|LIB99 |
+--+------+
The LIBxx/INVOICE tables are all identical to each other and have this structure:
CREATE OR REPLACE TABLE INVOICE (
ID BIGINT GENERATED ALWAYS AS IDENTITY (START WITH 1),
PAYDAT INTEGER(6,0) NOT NULL,
AMOUNT DECIMAL(15,2) NOT NULL DEFAULT 0,
PRIMARY KEY(ID)
) RCDFMT INVOICER;
+--+------+------+
|ID|PAYDAT|AMOUNT|
+--+------+------+
| 1|180701|100.00|
| 2|180801| 35.00|
|..|......|......|
+--+------+------+
I want to generate a list of invoice amounts for all tenants for a given date:
180701 LIB01 100.00
180701 LIB02 140.00
180701 LIB03 74.00
…
Conceptually what I want to do is this (yes, I know this is invalid SQL):
SELECT PAYDAT, TENANT, AMOUNT
FROM $X.INVOICE
WHERE PAYDAT = 180701;
I want to pull data from the INVOICE table for each TENANT but I know the FROM clause cannot be dynamic like this. I’m sure this kind of query has a name but I don’t know what it is so I’m unable to effectively use a search engine to find what I need.
This would be trivial to solve with an RPGLE program but I need a pure SQL solution.
Please note - the LIBxx values CANNOT be hardcoded in any way. These values can change at any time.
To do what you want, you can use a stored procedure with an EXECUTE IMMEDIATE in a loop to build a result set. Something like this:
Note: this is not a complete cut and paste solution, but you can modify it to do what you want.
CREATE OR REPLACE PROCEDURE GETINVOICEAMOUNTS ( )
DYNAMIC RESULT SETS 1
LANGUAGE SQL
NOT DETERMINISTIC
MODIFIES SQL DATA
CALLED ON NULL INPUT
SET OPTION COMMIT = *NONE
BEGIN
DECLARE STMT VARCHAR(1024);
DECLARE RECORD_FOUND INTEGER DEFAULT 1;
DECLARE LIBRARY CHAR(10);
DECLARE C1 CURSOR FOR
SELECT TENANT FROM CORPORATE/TENANT;
DECLARE C2 CURSOR WITH RETURN FOR
SELECT * FROM SESSION.TMP ;
DECLARE CONTINUE HANDLER FOR NOT FOUND
SET RECORD_FOUND = 0;
DECLARE GLOBAL TEMPORARY TABLE TMP
(PAYDAT INTEGER(6,0),
TENANT CHAR(10),
AMOUNT DECIMAL(15,2))
WITH REPLACE;
OPEN C1;
LOOP
FETCH C1 INTO LIBRARY;
IF RECORD_FOUND = 0;
LEAVE LOOP;
END IF;
SET STMT = 'INSERT INTO SESSION.TMP SELECT PAYDAT, LIBRARY, AMOUNT FROM ' || RTRIM(LIBRARY) || '.INVOICE WHERE PAYDAT = 180701';
EXECUTE IMMEDIATE STMT
END LOOP;
CLOSE C1;
OPEN C2;
END;
I gave you more than I planned to. But, one specific modification you will invariably need is to parameterize the date that you want to retrieve.
This is how it works:
A global temporary table named TMP is used to collect the records to be returned in a result set. Once all the records are collected, a cursor is opened over TMP and the procedure ends. This causes the values collected in TMP to be returned as a result set.
To collect the values the CORPORATE/TENANT file is read, and the column TENANT is retrieved into the variable LIBRARY. For each record a statement is built that concatenates LIBRARY into an INSERT statement. This statement is executed which loads the record into TMP. I am using EXECUTE IMMEDIATE because I cannot use a parameter marker to replace the table reference in the INSERT statement, so a prepared statement is just extra work.
You could use UNION ALL:
SELECT sub.PAYDAT, t.TENANT, sub.AMOUNT
FROM (SELECT * FROM LIB01.INVOICE
UNION ALL
SELECT * FROM LIB02/INVOICE
...
SELECT * FROM LIB0n/INVOICE) sub
JOIN TENANTS t
ON sub.id = t.id
WHERE SUB.PAYDAT = 180701;
It is an SELECT * FROM sales + #yymm template.
EDIT:
More secure way is to create a view:
CREATE VIEW combined_invoice
AS
SELECT * FROM LIB01.INVOICE
UNION ALL
SELECT * FROM LIB02/INVOICE
...
SELECT * FROM LIB0n/INVOICE;
And query:
SELECT sub.PAYDAT, t.TENANT, sub.AMOUNT
FROM combined_invoice sub
JOIN TENANTS t
ON sub.id = t.id
WHERE SUB.PAYDAT = 180701;
Of course view should be altered after adding/removing tables.

how to incorporate dynamic column name in sql query

I have a table, which has columns, say
Week1,Week2, Week3 and so on.
I have a stored procedure, and based on the number input, i want to select that column.
Example, if input is 4 then I want to make the query,
select *
from table_name
where Week4=<something>
Is there any way to do this other than using dynamic query? Because this dynamic thing will be just a small part of a huge query.
The comments about normalization are right, but if you have no choice, you can use "or" clauses:
declare #inputvalue int;
set #int = 1;
select *
from <table>
where (week1 = <something> and #inputvalue = 1)
or (week2 = <something> and #inputvalue = 2)
or (week3 = <something> and #inputvalue = 3)
or (week4 = <something> and #inputvalue = 4)
This will be very slow if the tables are of any size, as you won't be using any indexes. I wouldn't suggest doing this unless you're absolutely unable to change the table structure.
I realize this isn't what you asked for, but I figured I'd point out to some people who find this what you mean by doing this as a dynamic query.
You'd just write a procedure and hold the field name in there. Assuming that the naming standard is the same, so the input value would be the week# (1,2,7,27, 123, etc.) and the field name would directly correspond (Week1, Week2, Week7, Week27, Week123, etc.)
create or replace procedure myweek(week_in varchar2)
is
dyn_sql varchar2(1000);
begin
dyn_sql := 'select * from table_name where week'||week_in||' = ''something;'' '
execute immediate dyn_sql;
end;
/
Then to call it you'd just do something like :
exec myweek(27); and it would generate the sql:
select * from table_name where week27 = 'something';

How to use a common cursor in PLSQL script for multiple tables in same definition

There are 4 table in a DB as A, B, C and D. That tables has exact same structure.
I need to use same set of procedures to process those table and generate a single file
several utility procedures are available in my design i need to pass a row as a parameter for those procedure.
What is best way to implement a PLSQL script for that purpose ?
Please provide a high level overview?
Note:
my basic problem is use a common cursor and a common rowtype for this.
Oracle Native Dynamic SQL it's an easy way to query any table using fixed set of parameters and result variables (including records):
EXECUTE IMMEDIATE 'select ... from ' || table_name || ' where ....'
USING in_par_1, in_par2, OUT out_par_1, ...;
OPEN <cursor> FOR 'select ... from ' || table_name || ' where ....'
using ...;
LOOP
FETCH <cursor> INTO ...;
EXIT WHEN <cursor>%NOTFOUND;
...
END LOOP
(and others ways to dynamic data exchange are provided).
The biggest NDS problem is the lack of compile-time error checks, so it might be not easy to support it and there is a risk to break something and don't even notice that.
In your case one good trick can be used:
create a View which aggregates all data and marks its data source
create only one procedures set which depends on common data structure (the view) and data source ID (procedure's parameter).
The sample listing:
create or replace view view_abcd as
select 'a' as data_type, a.* from a
union
select 'b', b.* from b
union
select 'c', c.* from c
union
select 'd', d.* from d;
... procedure/function process_data( dataType ) ...
select .... from view_abcd v where v.data_type = dataType and ...