Related
I am testing an ETL load at the moment and want to create a temporary table that combines a number of rules so that I can do a minus against this temp table and the actual loaded table.
I've tried various things but keep getting the same message stating "FROM keyword is not found where expected"
My mentor is off on holiday and would appreciate if someone can offer me some assistance.
Create table tbl_HT_CUST_PHNUM_TEMP as
Select a.CONT_ID as CONT_ID,
a.TELEPHONE_NO as TELEPHONE_NO,
ROW_ID IS NULL,
STATUS_ID IS NULL,
SystemTableID IS NULL,
INITIAL_POPULATION_TS as INITIAL_POPULATION_TS,
BUSINESS_DATE_EXT as BUSINESS_DATE_EXT,
UPDATE_DATE_TIME as UPDATE_DATE_TIME,
DATE_ENDED as DATE_ENDED
CUSTOMERMAPPINGKEY IS NULL
from <schema>.tbl_HT_CUST_PHNUM a;
You have your ass confused with iss. Assuming your database supports create table as, then I suspect you want something like this:
Create table tbl_HT_CUST_PHNUM_TEMP as
Select a.CONT_ID as CONT_ID,
a.TELEPHONE_NO,
NULL as ROW_ID,
NULL as STATUS_ID,
NULL as SystemTableID,
a.INITIAL_POPULATION_TS,
a.BUSINESS_DATE_EXT,
a.UPDATE_DATE_TIME,
a.DATE_ENDED,
NULL as CUSTOMERMAPPINGKEY
from <schema>.tbl_HT_CUST_PHNUM a;
Note that you don't need as to specify a column name that is coming directly from a table.
I know that the question is very long and I understand if someone doesn't have the time to read it all, but I really wish there is a way to do this.
I am writing a program that will read the database schema from the database catalog tables and automatically build a basic application with the information extracted from the system catalogs.
Many tables in the database can be just a list of items of the form
CREATE TABLE tablename (id INTEGER PRIMARY KEY, description VARCHAR NOT NULL);
so when a table has a column that references the id of tablename I just resolve the descriptions by querying it from the tablename table, and I display a list in a combo box with the available options.
There are some tables however that cannot directly have a description column, because their description would be a combination of other columns, lets take as an example the most important of those tables in my first application
CREATE TABLE bankaccount (
bankid INTEGER NOT NULL REFERENCES bank,
officeid INTEGER NOT NULL REFERENCES bankoffice,
crc INTEGER NOT NULL,
number BIGINT NOT NULL
);
this as many would know, would be the full account number for a bank account, in my country it's composed as follows
[XXXX][XXXX][XX][XXXXXXXXXX]
^ ^ ^ ^
bank id | crc account number
|
|_ bank office id
so that's the reason of the way my bankaccount table is structured as is.
Now, I would like to have the complete bank account number in a description column so I can display it in the application without giving a special treatment to this situation, since there are some other tables with similar situation, something like
CREATE TABLE bankaccount (
bankid INTEGER NOT NULL REFERENCES bank,
officeid INTEGER NOT NULL REFERENCES bankoffice,
crc INTEGER NOT NULL,
number BIGINT NOT NULL,
description VARCHAR DEFAULT bankid || '-' || officeid || '-' || crc || '-' || number
);
Which of course doesn't work since the following error is raised1
ERROR: cannot use column references in default expression
If there is any different approach that someone can suggest, please feel free to suggest it as an answer.
1 This is the error message given by PostgreSQL.
What you want is to create a view on your table. I'm more familiar with MySQL and SQLite, so excuse the differences. But basically, if you have table 'AccountInfo' you can have a view 'AccountInfoView' which is sort of like a 'stored query' but can be used like a table. You would create it with something like
CREATE VIEW AccountInfoView AS
SELECT *, CONCATENATE(bankid,officeid,crc,number) AS FullAccountNumber
FROM AccountInfo
Another approach is to have an actual FullAccountNumber column in your original table, and create a trigger that sets it any time an insert or update is performed on your table. This is usually less efficient though, as it duplicates storage and takes the performance hit when data are written instead of retrieved. Sometimes that approach can make sense, though.
What actually works, and I believe it's a very elegant solution is to use a function like this one
CREATE FUNCTION description(bankaccount) RETURNS VARCHAR AS $$
SELECT
CONCAT(bankid, '-', officeid, '-', crc, '-', number)
FROM
bankaccount this
WHERE
$1.bankid = this.bankid AND
$1.officeid = this.officeid AND
$1.crc = this.crc AND
$1.number = this.number
$$ LANGUAGE SQL STABLE;
which would then be used like this
SELECT bankaccount.description FROM bankaccount;
and hence, my goal is achieved.
Note: this solution works with PostgreSQL only AFAIK.
I am having the below DDL for external table.
CREATE TABLE emp_load
(
employee_number VARCHAR2(50),
employee_last_name VARCHAR2(50),
employee_first_name VARCHAR2(50),
employee_middle_name VARCHAR2(50),
employee_hire_date VARCHAR2(50)
)
organization external (TYPE oracle_loader DEFAULT directory abc_dir ACCESS
parameters ( records
delimited BY newline fields terminated BY '|' missing
field VALUES are NULL (employee_number, employee_last_name
, employee_first_name, employee_middle_name,
employee_hire_date) ) location ('info.dat') ) reject limit
UNLIMITED
and my .dat file is like below.
010|ABC|DEF|XYZ|03-DEC-2011
020|CCC|123|SSS|04-DEC-2011
I have a table called
CREATE TABLE test_emp_load_1
(
mployee_number VARCHAR2(50),
employee_last_name VARCHAR2(50),
employee_first_name NUMBER(38),
employee_middle_name VARCHAR2(50),
employee_hire_date VARCHAR2(50)
)
and now i am using the below merge statement ( in the below even though i keep e.EMPLOYEE_NUMBER = '020' i think first it trying to run a scan on the entire external table)
which is giving the below error.
SQL Error: ORA-29913: error in executing ODCIEXTTABLEFETCH callout
ORA-01722: invalid number
but when i am using
MERGE INTO test_emp_load_1 te
USING (select * from emp_load where EMPLOYEE_NUMBER = '020') e
on ( e.EMPLOYEE_FIRST_NAME = te.employee_first_name )
WHEN MATCHED THEN
UPDATE SET
te.employee_last_name = e.EMPLOYEE_LAST_NAME
WHEN NOT MATCHED THEN
INSERT
(te.employee_last_name)
VALUES
( e.EMPLOYEE_LAST_NAME)
where e.EMPLOYEE_NUMBER = '020';
I am getting output 1 row merged. It looks a bug in Oracle 11g R2.
I am using DB Oracle 11G R2 on Windows Platform. I also tried this in Red hat Linux and Oracle 11g R2 I am getting the same issue
Any suggestions?
In your external table you have:
employee_first_name VARCHAR2(50),
In your other table you have:
employee_first_name number(38),
In your merge you have:
on ( e.EMPLOYEE_FIRST_NAME = te.employee_first_name )
So you're comparing a string with a number. They have to be compared as the same type; it could go either way, but Oracle is choosing to convert the string into a number to do the comparision, so it's effectively doing:
on ( to_number(e.EMPLOYEE_FIRST_NAME) = te.employee_first_name )
If your data is actually numeric then that is OK, though it would be better to have the data types right in the first place. But your data is not numeric, and probably isn't really meant to be. Look at your sample data again:
010|ABC|DEF|XYZ|03-DEC-2011
020|CCC|123|SSS|04-DEC-2011
The 'first name' is the third field in the file. The second row is OK as '123' can be converted to a number. The first row is not OK, 'DEF' cannot be converted to a number. That row is therefore rejected. This probably isn't the field you meant to be numeric in the first place though, given its name.
As Ben mentioned you have the mployee_number field named incorrectly in your normal table, so that will also error at some point. Just to avoid those errors your table would need to be defined like this:
create table test_emp_load_1 (employee_number NUMBER,
employee_last_name VARCHAR2(50),
employee_first_name VARCHAR2(50),
employee_middle_name VARCHAR2(50),
employee_hire_date DATE)
Assuming all the records will actually have a numeric first field, and a valid date as the last field. Your external table definition should also define the columns with the correct type, and specify the expected date format so that doesn't error. You should always use the correct data types: never store numbers or dates as strings, even in an external table definition (though they are obviously string in the actual external file).
The merge also seems odd as you're only setting the last name for inserted records.
I am curious if I could use the type of an existing table's column when I create another column. Just as using a data type like varchar, I would like to have my column the same type as a column in another table.
I am imagining something like
CREATE TABLE FIRST (id NUMBER(5), name VARCHAR(25))
and then
CREATE TABLE SECOND (id NUMBER(6), value FIRST.NAME%TYPE)
and the type of the VALUE column would be VARCHAR(25)
I see this as a generic SQL question, though I am using Oracle.
You can do the following:
CREATE TABLE SECOND AS (
SELECT ID, NAME AS VALUE
FROM FIRST
WHERE 1 = 2
);
If think the %type syntax is a plsql thing only
In standard SQL, you'd write a CREATE DOMAIN statement.
CREATE DOMAIN my_name_type VARCHAR(25);
But I don't think Oracle supports CREATE DOMAIN, so you I think you need to create an object instead.
create type my_name_type as object
( my_name_col varchar2(25));
In either case, you'd use it directly in creating a table.
create table test (
user_name my_name_type
);
I recall that the syntax for INSERT statements is a little weird; check the docs for that.
You could do this in SQL Server by using SELECT INTO:
SELECT TOP 0 CAST(0 as int) as 'id', Field
INTO NewTable
FROM OtherTable
I'm trying to write a stored procedure for Crystal Reports by combining multiple queries into a single resultset (Crystal doesn't support multiple results in one report).
The result set I'm trying to get combines columns from both tables.
In the SP, I declare #temptable and the columns (because the two tables i'm querying have different columns).
DECLARE #TEMPNEWBILLING TABLE
(
ACCOUNT DECIMAL null,
CLIENT NVARCHAR null,
TIMESTAMP INT null,
BILLING DECIMAL null,
CALLKIND INT null,
HITK1 DECIMAL null,
HITK2 DECIMAL null,
HIDISC DECIMAL null,
HITALK DECIMAL null,
HIPTCH DECIMAL null,
HICONF DECIMAL null,
HIHOLD DECIMAL null,
PTCH DECIMAL null,
SUPERTIME DECIMAL null
)
I then SELECT from both tables INTO the temp table:
SELECT Account, Client, Timestamp, Billing, CallKind, HiTk1, HiTk2, HiDisc, HiTalk, HiPtch, HiConf, HiHold, Ptch
INTO TEMPNEWBILLING
FROM
mCallEnd
WHERE billing = cast(#BILLINGNUMBER as decimal)
AND Timestamp > #STARTITIME
AND Timestamp < #ENDITIME
AND CallKind in (0,1,2,3,4,16)
SELECT
Billing, SuperTime
INTO TEMPNEWBILLING
FROM
mClientMaint
WHERE billing = cast(#BILLINGNUMBER as decimal)
AND Timestamp > #STARTITIME
AND Timestamp < #ENDITIME
And finally, I just get all data from the temp table.
SELECT * FROM #TEMPNEWBILLING
Unfortunately, something is going wrong, as when I run the SP, I get an error that
There is already an object named 'TEMPNEWBILLING' in the database.
I've checked it out and it seems that the first query is running, but the error gets thrown at the second Select Into. I must be doing this wrong, as I get the same error if I use # tables or # tables (i.e. delcare the table vs. create the table).
Is the prospect of filling a temp table with the results of two queries simply not possible? Am I using the wrong tool for the job?
SELECT... INTO creates a new table.
You'll want to reverse it:
INSERT INTO #TEMPNEWBILLING
(Columns...)
SELECT (your select query here)
You'll want to declare the table (technically it's a table variable since you're using the # sign) as you did. Then use INSERT INTO... SELECT... for all of your inserts.
In your code, you are not using the variable table youe defined, instead you are trying to put the results into the same physical table. Try this instead:
INSERT INTO #TEMPNEWBILLING(Account, Client, Timestamp, Billing, CallKind, HiTk1, HiTk2, HiDisc, HiTalk, HiPtch, HiConf, HiHold, Ptch)
SELECT Account, Client, Timestamp, Billing, CallKind, HiTk1, HiTk2, HiDisc, HiTalk, HiPtch, HiConf, HiHold, Ptch
FROM
mCallEnd
WHERE billing = cast(#BILLINGNUMBER as decimal)
AND Timestamp > #STARTITIME
AND Timestamp < #ENDITIME
AND CallKind in (0,1,2,3,4,16)
INSERT INTO #TEMPNEWBILLING(Billing, SuperTime)
SELECT
Billing, SuperTime
FROM
mClientMaint
WHERE billing = cast(#BILLINGNUMBER as decimal)
AND Timestamp > #STARTITIME
AND Timestamp < #ENDITIME
You need to use INSERT once the table is already created. Also, you're using a table variable, so you need to include the # at the beginning of the name when referring to it. Since you're declaring the table variable at the start, both statements should actually be INSERT and not SELECT INTO.
SELECT INTO tries to create a new table. In your code you basically declare a table variable (which never gets used), then your first SELECT INTO creates a permanent table with the name TEMPNEWBILLING, then your second SELECT INTO tries to create a table with the same exact name - hence the error.