Snowflake SQL Compilation Error: View Definition Declared but view Query Produced - sql

I've just gotten a new query error that I haven't changed anything to. Any advice on what to do? Thanks
SQL compilation error:
View definition for '**********' declared 115 column(s), but view query produces 117 column(s).

This is speculation, but it sounds like your view is using select x.*, where * means to get all the columns from some table.
Then, the underlying table changes . . . and voila, you might have a problem.

I've just gotten a new query error that I haven't changed anything to. Any advice on what to do?
If the query started to produce errors it means that the defintion of view is no longer "valid/up-to-date". Most likely the underlying table has been altered.
CREATE VIEW
View definitions are not dynamic. A view is not automatically updated if the underlying sources are modified such that they no longer match the view definition, particularly when columns are dropped. For example:
A view is created referencing a specific column in a source table and the column is subsequently dropped from the table.
A view is created using SELECT * from a table and any column is subsequently dropped from the table.
In either of these scenarios, querying the view returns a column mismatch error.
Steps to reproduce the scenario:
CREATE OR REPLACE TABLE t(col1 INT, col2 INT);
INSERT INTO t(col1, col2) VALUES (1,1);
CREATE OR REPLACE VIEW v_t AS SELECT * FROM t;
SELECT * FROM v_t;
--COL1 COL2
--1 1
So far so good. Now altering the underlying table by adding new column:
ALTER TABLE t ADD COLUMN col3 INT DEFAULT 3;
SELECT * FROM v_t;
SQL compilation error: View definition for 'V_T' declared 2 column(s), but view query produces 3 column(s).
Recreation of the view and keeping its definition on par with underlying tables should resolve it:
CREATE OR REPLACE VIEW v_t
COPY GRANTS
AS
SELECT * FROM t;
-- using * will work to refresh it but I would not recommend it
-- and explicitly describe columns instead
CREATE OR REPLACE VIEW v_t
COPY GRANTS -- to preserve already granted permissions
AS
SELECT col1, col2, col3 FROM t;
SELECT * FROM v_t;
-- COL1 COL2 COL3
-- 1 1 3

I found myself with a similar issue this morning. I had copied my query from a txt file I had and pasted it into a worksheet and tired to run it and got the same error. I had used the Table with the definition issue for a join and only for 1 column so I didn't see why it was giving me such an error.
All a look of check tables I commented on the Worksheet the error I was seeing. But I decided to run it again and it worked.
This tells me that Snowflake was using what it had cached but after editing the query it saw it as a new query and re-ran it instead of erroring out when what is in the cache doesn't match the definition.

Related

There is already an object named '#tmptable' in the database

I´m trying to execute stored procedure but I get an issue of an existing temporal table, but I just create one time and use into another part of code
SELECT ...
INTO #tmpUnidadesPresupuestadas
FROM proce.table1
--Insertar in table src..
INSERT INTO table (
....)
SELECT
....
FROM
#tmpUnidadesPresupuestadas
I get this message:
There is already an object named
'#tmpUnidadesPresupuestadas' in the database.
How can I solve it? Regards
A temp table lives for the entirety of the current session. If you run this statement more than once, then the table will already be there. Either detect that and truncate it, or before selecting into it drop it if it exists:
DROP TABLE IF EXISTS #tmpUnidadesPresupuestadas
If prior to SQL Server 2016, then you drop as such:
IF OBJECT_ID('tempdb.dbo.#tmpUnidadesPresupuestadas', 'U') IS NOT NULL
DROP TABLE #tmpUnidadesPresupuestadas;
Without seeing more of the code, it's not possible to know if the following situation is your problem, but it could be.
When you have mutually exclusive branches of code that both do a SELECT...INTO to the same temp table, a flaw causes this error. SELECT...INTO to a temp table creates the table with the structure of the query used to fill it. The parser assumes if that occurs twice, it is a mistake, since you can't recreate the structure of the table once it already has data.
if #Debug=1
select * into #MyTemp from MyTable;
else
select * into #MyTemp from MyTable;
While obviously not terribly meaningful, this alone will show the problem. The two paths are mutually exclusive, but the parser thinks they may both get executed, and issues the fatal error. You extend that, wrapping each branch in a BEGIN...END, and add the drop table (conditional or not) and the parser will still give the error.
To be fair, in fact both paths COULD be executed, if there were a loop or GOTO so that one time around #Debug = 1, and the other time it does not, so it may be asking too much of a parser. Unfortunately, I don't know of a workaround, and using INSERT INTO instead of SELECT INTO is the only way I know to avoid the problem, even though that can be terribly onerous to name all the columns in a particularly column-heavy query.
I am a bit unclear as to what you are attempting. I assume you don't want to drop the table at this point. I believe the syntax you may be looking for is
Insert Into
Insert into #tmpUnidadesPresupuestadas (Col1, col2, ... colN)
Select firstcol, secondcol... nthCol
From Data
If you do indeed wish to drop the table, the previous answers have that covered.
This might be useful for someone else, keep in mind that If more than one temporary table is created inside a single stored procedure or batch, they must have different names. If you use the same name you won't be able to ALTER the PROCEDURE.
https://learn.microsoft.com/en-us/previous-versions/sql/sql-server-2012/ms174979(v=sql.110)#temporary-tables
Make sure the stored procedure and the table doesn't have same name.
Add logic to delete if exists. Most likely you ran it previously. The table remains from the previous running of the stored procedure. If you log out and log in then run it, that would likely clear it. But the cleanest way is to check if it exists and delete it if it does. I assume this is MsSql.
At first you should check if temp table is already exist if yes then delete it then create a empty table then use insert statement. refer below example.
IF OBJECT_ID('tempdb..#TmpTBL') IS NOT NULL
DROP TABLE #TmpTBL;
SELECT TOP(0) Name , Address,PhoneNumber
INTO #TmpTBL
FROM EmpDetail
if #Condition=1
INSERT INTO #TmpTBL (Name , Address,PhoneNumber)
SELECT Name , Address,PhoneNumber FROM EmpDetail;
else
INSERT INTO #TmpTBL (Name , Address,PhoneNumber)
SELECT Name , Address,PhoneNumber FROM EmpDetail;

Statement Execution order in SQL Server SQL Script

I am having trouble with a sql script that seems to be related to the execution order of the statements or possibly just error checking that sql server does prior to starting.
This is a simplification of the code but for background, the table finalTable is cleared and repopulated by this script. It already exists in the DB but does not have new columns that are being added. For reference in the example, pretend Col1 is an existing column and Col2 is new.
If I run the code all together, I get a message saying Invalid Column name 'Col2'. If I run each block individually, everything works fine.
Block A:
SQL to create temporary tables
Block B:
drop table dbo.finalTable;
create table dbo.finalTable (col1 int, col2 int);
Block C:
insert into dbo.finalTable(col1, col2) select col1, col2 from #tempTable;
The script snippets you posted are not detailed enough to pinpoint the exact cause of the error but the symptoms suggest deferred name resolution. The issue is not statement execution order but compilation order.
SQL Server checks for syntax errors when a batch of statements is submitted for execution. If syntactically correct, statements referencing existing objects are validated against existing schema. Compilation of statements referencing non-existing objects are deferred until execution time. Deferred name resolution allows one to create a table and use it in the same batch:
CREATE TABLE dbo.finalTable (col1 int);
--Compilation of this statement is not done until execution time due to deferred name resolution
SELECT col1 FROM dbo.finaltable;
GO
Compilation of the entire batch will fail when a statement references a non-existing column of an existing table. No statements, including the first SELECT ALTER TABLE, are executed in this batch because col2 does not exist when the batch is compiled:
SELECT col1 FROM dbo.finaltable;
ALTER TABLE dbo.finaltable
ADD col2 int NULL;
SELECT col1, col2 FROM dbo.finaltable;
GO
Is it the same database the script execution and finalTable exists when you executed the script as whole? You may try to
IF OBJECT_ID('databasename.dbo.finalTable', 'U') IS NOT NULL
DROP TABLE databasename.dbo.finalTable;

Oracle - Zombie Table

I'm having this odd problem since yesterday. I've tried several options and I actually reinstalled ORACLE and the DB itself.
Here's the problem: I have this table that is somekind of zombie. Here are the symptoms:
SELECT TABLE_NAME FROM USER_TABLES WHERE TABLE_NAME='MYTABLE'
Returns a record, meaning that the table exists.
SELECT COLUMN_NAME FROM USER_TAB_COLUMNS WHERE TABLE_NAME = 'MYTABLE'
Returns all the columns of MYTABLE. So far so good, the table exists.
SELECT * FROM MYTABLE
Returns ORA-00942: table or view does not exist.
At this point I'm quite confused: the table seems to exist on the USERTABLES but I cannot SELECT over it?
CREATE TABLE MYTABLE (Foo NUMBER) TABLESPACE MYTABLESPACE
Returns:
ORA-00604: error occurred at recursive SQL level 1
ORA-00001: unique constraint (SYS.I_OBJ2) violated
I do not understand this error. But the best is yet to come.
SELECT * FROM MYTABLE
Surprisingly, the above query (an exact copy of the 3rd query) returns several records now!
Moreover, I noticed that the column Foo is not present: the table I now see is my initial table that had other columns.
DROP TABLE MYTABLE
I now try to drop the table and I get the following errors:
ORA-00604: error occurred at recursive SQL level 1
ORA-00942: table or view does not exist
ORA-06512: at line 19
SELECT * FROM MYTABLE
More confused than ever, I try the above query and, surprise surprise, the table no longer exists.
I don't undestand this: the table is on USERTABLES but I cannot SELECT over it, however, if I create a new table with the same name, I get an error but now I can SELECT over the previous version of that table with several records.
Any thoughts ? I really need your help :(
EDIT - I checked now: I'm unable to drop ANY table. This might just be a new symptom.
Solution
The problem was that MDSYS.SDO_GEOR_SYSDATA_TABLE table was missing and a drop event trigger was trying to access it, generating the error. The solution was restoring that table.
If have privileges, try this query:
SELECT *
FROM dba_objects
WHERE object_name = 'MYTABLE';
And see what objects exist with that name. It might point you in the right direction.
You didn't qualify the schema names when trying to select and drop. The CURRENT_SCHEMA of your session may be different form the log-on user. Check by trying
select SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA') from dual;
Instead of describing what the output was, could you please copy/paste the complete output for us?
Lastly, can you exclude that someone messed up the dictionary? You know, SYSDBA can do anything....

How to auto-redefine view when underlying table changes (new column)?

We've got a view that's defined like this
CREATE VIEW aView as
SELECT * from aTable Where <bunch of conditions>;
The "value" of the view is in the where-condition, so it is okay to use a Select * in this case.
When a new column is added to the underlying table, we have to redefine the view with a
CREATE OR REPLACE FORCE VIEW aView as
SELECT * from aTable Where <bunch of conditions>;
as the Select * seems to get "translated" into all the columns present at the time the view is (re-)defined.
My question: How can we avoid this extra step?
(If the answer is dependent on the RDBMS, we're using Oracle.)
I know you specified Oracle, but the behavior is the same in SQL Server.
One way to update the view with the new column is to use:
exec sp_refreshview MyViewName
go
Of course, I also agree with the other comments about not using a SELECT * in a view definition.
This extra step is mandatory in Oracle: you will have to recompile your view manually.
As you have noticed, the "*" is lost once you create a view:
SQL> create table t (id number);
Table created
SQL> create view v as select * from t;
View created
SQL> select text from user_views where view_name = 'V';
TEXT
-------------------------------------------------------
select "ID" from t
You should not be using * in your views. Specify the columns explicitly.
That way you are only retrieving the data you need, and thus avoid potential issues down the road where someone adds a column to a table that you do not want that view to return (e.g., a large binary column that would adversely impact performance).
Yes, you need to recompile the view to add another column, but this is the correct process. That way you avoid other compilation issues, such as if the view reference two tables, and someone adds a duplicate column name in one of the tables. The compiler would then have issues determining which of the columns was being referred to if you did not prefix a reference to the column with a table alias, or it might complain if there are duplicate column names in the results.
The problem with automatically updating views to add columns comes when you extend your model, for example to
SELECT a.*, std_name_format(a.first_name, a.middle_names, a.last_name) long_name
or even
SELECT a.*, b.* from table_a a join table_b b....
If you have a view of just SELECT * FROM table, then you probably should be using a synonym or addressing the table directly.
If the view is hiding rows (SELECT * FROM table WHERE...), then you can look at the feature variously known as Fine Grained Access Control (FGAC), Row Level Security (RLS) or Virtual Private Database (VPD).
You might be able to do something with a DDL trigger but that would get complicated.

How can I create a copy of an Oracle table without copying the data?

I know the statement:
create table xyz_new as select * from xyz;
Which copies the structure and the data, but what if I just want the structure?
Just use a where clause that won't select any rows:
create table xyz_new as select * from xyz where 1=0;
Limitations
The following things will not be copied to the new table:
sequences
triggers
indexes
some constraints may not be copied
materialized view logs
This also does not handle partitions
I used the method that you accepted a lot, but as someone pointed out it doesn't duplicate constraints (except for NOT NULL, I think).
A more advanced method if you want to duplicate the full structure is:
SET LONG 5000
SELECT dbms_metadata.get_ddl( 'TABLE', 'MY_TABLE_NAME' ) FROM DUAL;
This will give you the full create statement text which you can modify as you wish for creating the new table. You would have to change the names of the table and all constraints of course.
(You could also do this in older versions using EXP/IMP, but it's much easier now.)
Edited to add
If the table you are after is in a different schema:
SELECT dbms_metadata.get_ddl( 'TABLE', 'MY_TABLE_NAME', 'OTHER_SCHEMA_NAME' ) FROM DUAL;
create table xyz_new as select * from xyz where rownum = -1;
To avoid iterate again and again and insert nothing based on the condition where 1=2
Using sql developer select the table and click on the DDL tab
You can use that code to create a new table with no data when you run it in a sql worksheet
sqldeveloper is a free to use app from oracle.
If the table has sequences or triggers the ddl will sometimes generate those for you too. You just have to be careful what order you make them in and know when to turn the triggers on or off.
You can do this
Create table New_table as select * from Old_table where 1=2 ;
but be careful
The table you create does not have any Index, PK and so on like the old_table.
DECLARE
l_ddl VARCHAR2 (32767);
BEGIN
l_ddl := REPLACE (
REPLACE (
DBMS_LOB.SUBSTR (DBMS_METADATA.get_ddl ('TABLE', 'ACTIVITY_LOG', 'OLDSCHEMA'))
, q'["OLDSCHEMA"]'
, q'["NEWSCHEMA"]'
)
, q'["OLDTABLSPACE"]'
, q'["NEWTABLESPACE"]'
);
EXECUTE IMMEDIATE l_ddl;
END;
Simply write a query like:
create table new_table as select * from old_table where 1=2;
where new_table is the name of the new table that you want to create and old_table is the name of the existing table whose structure you want to copy, this will copy only structure.
SELECT * INTO newtable
FROM oldtable
WHERE 1 = 0;
Create a new, empty table using the schema of another. Just add a WHERE clause that causes the query to return no data:
WHERE 1 = 0 or similar false conditions work, but I dislike how they look. Marginally cleaner code for Oracle 12c+ IMHO is
CREATE TABLE bar AS
SELECT *
FROM foo
FETCH FIRST 0 ROWS ONLY;
Same limitations apply: only column definitions and their nullability are copied into a new table.
If one needs to create a table (with an empty structure) just to EXCHANGE PARTITION, it is best to use the "..FOR EXCHANGE.." clause. It's available only from Oracle version 12.2 onwards though.
CREATE TABLE t1_temp FOR EXCHANGE WITH TABLE t1;
This addresses 'ORA-14097' during the 'exchange partition' seamlessly if table structures are not exactly copied by normal CTAS operation. I have seen Oracle missing some of the "DEFAULT" column and "HIDDEN" columns definitions from the original table.
ORA-14097: column type or size mismatch in ALTER TABLE EXCHANGE
PARTITION
See this for further read...
you can also do a
create table abc_new as select * from abc;
then truncate the table abc_new. Hope this will suffice your requirement.
Using pl/sql developer you can right click on the table_name either in the sql workspace or in the object explorer, than click on "view" and than click "view sql" which generates the sql script to create the table along with all the constraints, indexes, partitions etc..
Next you run the script using the new_table_name
copy without table data
create table <target_table> as select * from <source_table> where 1=2;
copy with table data
create table <target_table> as select * from <source_table>;
In other way you can get ddl of table creation from command listed below, and execute the creation.
SELECT DBMS_METADATA.GET_DDL('TYPE','OBJECT_NAME','DATA_BASE_USER') TEXT FROM DUAL
TYPE is TABLE,PROCEDURE etc.
With this command you can get majority of ddl from database objects.
Create table target_table
As
Select *
from source_table
where 1=2;
Source_table is the table u wanna copy the structure of.
create table xyz_new as select * from xyz;
-- This will create table and copy all data.
delete from xyz_new;
-- This will have same table structure but all data copied will be deleted.
If you want to overcome the limitations specified by answer:
How can I create a copy of an Oracle table without copying the data?
The task above can be completed in two simple steps.
STEP 1:
CREATE table new_table_name AS(Select * from old_table_name);
The query above creates a duplicate of a table (with contents as well).
To get the structure, delete the contents of the table using.
STEP 2:
DELETE * FROM new_table_name.
Hope this solves your problem. And thanks to the earlier posts. Gave me a lot of insight.