Vertica - Work around for using WITH with INSERT statement - sql

I have a SQL query like
with subtable as (
................
)
select *
from subtable
I wanted to insert the records from the select statement into a table. looking for something like :
with subtable as (......)
insert into newtable
select *
from subtable
However, in Vertica, INSERT cannot be used with the WITH ( CTE) statements.
Is there any work around for this?
Thanks for the help

with is part of the select.
insert into newtable
with subtable as (......)
select *
from subtable

I can only agree to what woot said.
In Vertica, the common table expression is supported in the SELECT statement - not in any DML statement. That's also what the ANSI standard intended.
If your point is to have the data bit at the top of the script and not "hidden" by the INSERT INTO foo line before, then you can (but still with one line before the WITH clause):
CREATE LOCAL TEMPORARY TABLE foo(id,ts,name,exp) ON COMMIT PRESERVE ROWS AS
SELECT 1,'2016-12-13 10:11'::TIMESTAMP,'Moshe',1
UNION ALL SELECT 2,'2016-12-13 12:12'::TIMESTAMP,'Karl' ,2
UNION ALL SELECT 3,'2016-12-13 13:12'::TIMESTAMP,'Karl' ,2
UNION ALL SELECT 4,'2016-12-13 14:09'::TIMESTAMP,'Moshe',2
UNION ALL SELECT 5,'2016-12-13 18:07'::TIMESTAMP,'Karl' ,2
KSAFE 0;
Marco the Sane

Related

Trying to filter on a union of 3 queries

I have a union of 3 queries that summarizes like this...
Select param1 As 'example1' And .... Where...
Union All
Select param1 As 'example2' And .... Where...
Union All
Select param1 As 'example3' And .... Where...
Is there any way to wrap this in a Select and create an optional parameter that filters on example1/example2/example3?
Any help would be greatly appreciated. Thanks!
Sorry everyone...when I was brainstorming in my head, I had my query wrong and I'm not very good at sql anyway. But what I want to do was filter on the created column of AccountStatus as an optional parameter. Is there some way to capture all this and then add an optional parameter to filter on the created column?
Select 'Red Account' As AccountStatus And .... Where OverdueDays >= 30
Union All
Select 'Yellow Account' As AccountStatus And .... Where 10 < OverdueDays < 30
Union All
Select 'Green Account' As AccountStatus And .... Where OverdueDays <= 10
Is there any way to wrap this in a Select and create an optional parameter that filters on example1/example2/example3?
Assuming that I understood your need then you can use common table expression (CTE).
In the following document you can read more about the option of using CTE:
https://learn.microsoft.com/en-us/sql/t-sql/queries/with-common-table-expression-transact-sql?view=sql-server-ver15
In general, you wrap your code into a CTE (in the following example I will name my CTE MyCTE but you can use any name), and then in the outside query you can add the filter which you want.
Do not confuse between CTE and a variable or temporary table. A CTE is not a physical entity but logically for the sake pf the query and the server will parse it as inline code.
For example:
;With MyCTE as ( <here comes the code you want to wrap> )
SELECT <choose columns to select>
FROM MyCTE
<here you can add order by or filetring with where>
In your case it might look like:
;With MyCTE as (
-- when you use UNION then the name of the columns in the result is configured by the names in the first query, so you do not need all the "as..." for the other queries.
-- not clear why you use "And" in the name of the columns you select. If you need more than one column then you should use comma "," between the columns
Select param1 As [example1], column2, column3... Where... <this filter only this specific query>
Union All
Select param1, column2, column3.... Where...
Union All
Select param1, column2, column3.... Where...
)
SELECT param1, column2, column3....
FROM MyCTE -- we select from the logical CTE as it was a table
WHERE <here you can add condition to filter the result of the UNION>
Yes it is possible. And here is where you should make the effort provide consumable information that you and everyone else can use as a basis for a solution. Below is the code in this fiddle which you can play with.
-- "parameters"
--declare #p1 varchar(20) = null;
declare #p1 varchar(20) = 'waggle';
-- some crap data
declare #x table (id int, trandate date);
insert #x (id, trandate) values
(1, '20190101'), (2, '20190425'),
(4, '20200311'), (5, '20200630'), (11, '20200801'),
(12, '20210101'), (13, '20210710');
with crap_union as (
select 'wiggle' as [when], id, trandate
from #x where year(trandate) = 2019
union all
select 'waggle' as [when], id, trandate
from #x where year(trandate) = 2020
union all
select 'waddle' as [when], id, trandate
from #x where year(trandate) = 2021
)
select * from crap_union
where [when] = #p1 or #p1 is null
order by [when], id, trandate
;
Same idea as generically expressed by Ronen. You add some sort of identifier to each query in the UNION. That allows you to "see" which row comes from which query. You stuff that UNION into a CTE or a derived table (same logical effect) and then select from that CTE (or derived table) with the desired filter.
You might consider reading Erland's discussion on dynamic search conditions to understand the performance considerations of this approach. Bookmark Erland's website since it has much useful information.

In SQL Server, how can I perform a series of queries without creating intermediary tables with different names or renaming/dropping?

Imagine I want to apply sequentially two different queries to a table:
select *
into #temp_table1
from original_table
...
select *
into #temp_table2
from #temp_table1
...
I don't really need the intermediary temp_table1, but it's an input to create temp_table2. Also assume I can't merge both queries and don't want to create nested queries (since the sequence can be infinitely long).
I'd rather be able to implicitly overwrite it and write something like:
select *
into #temp_table
from original_table
...
select *
into #temp_table
from #temp_table
...
This way, I wouldn't need to explicitly create the intermediary step tables. It makes easier to write and modify a script. I'm doing this solely for the purpose of analysis, I won't be creating indexes or constraints for these temporary tables.
I could do it with sp_rename and drop, but it's cumbersome to do it after every query. Is there a smarter way?
After your latest question edit, I can see the CTE (Common Table Expression) as being the last solution. Below is a solution with nested CTE's.
;with temp_table1 as (
select *
from original_table
...)
, temp_table2 as (
select *
from temp_table1
...)
, temp_table3 as (
select *
from temp_table2
... )
, master_CTE as (
select *
from temp_table3
...)
select *
from master_CTE
This will allow you to have a "recursive" and "infinite" pattern of writing your query.
The only thing you want to make sure is that the last part of your query is a SELECT from your last "CTE", I've called mine "master_CTE" for the lack of a better idea.
If you do however want to persist your final results it's as easy as adding the INTO clause, like so:
, master_CTE as (
select *
from temp_table3
...)
select *
into persisted_master_CTE
from master_CTE;
Based on your comment:
You don't really want to modify "all indices" because you probably don't care about the naming sequence, only the execution sequence. You add the new "processing" intermediary CTE and then adjust the references.
It's just like adding an element to a single linked list (if you try to view this differently).
So, you can name them like so:
;with temp_table1 as (
select *
from original_table
...)
, temp_table2 as (
select *
from temp_table1
...)
, intermediary_step ( -- <-- since you needed to do some additional analysis here
select *
from temp_table2
... )
, temp_table3 as (
select *
from intermediary_step -- <-- the intermediary step
... )
, master_CTE as (
select *
from temp_table3
...)
select *
from master_CTE

"ORA-00984: column not allowed here" when inserting with select statement

I would like to insert some data into a table. One field I would like to get from another table, so I'm using select statement inside. This is the code:
INSERT INTO t.table1 (
id,
id_t2,
date_of_change
)
VALUES (
t.table1_seq.nextval,
SELECT s.id_id_t2 from t.table2 s where s.something='something',
TO_DATE('02/05/2017 13:43:34','DD/MM/YYYY HH24:MI:SS')
)
Although select statement is always returning only 1 field (1 row), I presume this is why I'm getting the error.
How can I write INSERT statement with SELECT statement for just 1 field? Can it be done? If not, is there any other solution for this problem? Thank you.
You can translate your whole insert statement into the form of
insert into table1 (fields)
select fields from table2
This will allow you to specify in your select some values from the source table and some constant values. Your resulting query would be
INSERT INTO t.table1 (
id,
id_t2,
date_of_change
)
SELECT t.table1_seq.nextval,
s.id_id_t2,
TO_DATE('02/05/2017 13:43:34','DD/MM/YYYY HH24:MI:SS')
FROM t.table2 s
WHERE s.something='something'

Insert Multiple Rows SQL Teradata

I am creating a volatile table and trying to insert rows to the table. I can upload one row like below...
create volatile table Example
(
ProductID VARCHAR(15),
Price DECIMAL (15,2)
)
on commit preserve rows;
et;
INSERT INTO Example
Values
('Steve',4);
However, when I try to upload multiple I get the error:
"Syntax error: expected something between ')' and ','."
INSERT INTO Example
Values
('Steve',4),
('James',8);
As Gordon said, Teradata doesn't support VALUES with multiple rows (and the UNION ALL will fail because of the missing FROM.
You can utilize a Multi Statement Request (MSR) instead:
INSERT INTO Example Values('Steve',4)
;INSERT INTO Example Values('James',8)
;
If it's a BTEQ job the Inserts are submitted as one block after the final semicolon (when there's a new command starting on the same line it's part of the MSR). In SQL Assistant or Studio you must submit it using F9 instead of F5.
I don't think Teradata supports the multiple row values syntax. Just use select:
INSERT INTO Example(ProductId, Price)
WITH dual as (SELECT 1 as x)
SELECT 'Steve' as ProductId, 4 as Price FROM dual UNION ALL
SELECT 'James' as ProductId, 8 as Price FROM dual;
CTE syntax (working):
insert into target_table1 (col1, col2)
with cte as (select 1 col1)
select 'value1', 'value2' from cte
union all
select 'value1a', 'value2a' from cte
;
CTE Syntax not working in Teradata
(error: expected something between ")" and the "insert" keyword)
with cte as (select 1 col1)
insert into target_table1 (col1, col2)
select 'value1', 'value2' from cte
union all
select 'value1a', 'value2a' from cte
;
I found a solution for this via RECURSIVE. It goes like this:-
INSERT INTO table (col1, col2)
with recursive table (col1, col2) as
(select 'val1','val2' from table) -- 1
select 'val1','val2' from table -- 2
union all select 'val3','val4' from table
union all select 'val5','val6' from table;
Data of line 1 does not get inserted (but you need this line). Starting from line 2, the data you enter for val1, val2 etc. gets inserted into the respective columns. Use as many UNION ALLs' as many rows you want to insert. Hope this helps :)
At least in our version of Teradata, we are not able to use an insert statement with a CTE. Instead, find a real table (preferably small in size) and do a top 1.
Insert Into OtherRealTable(x, y)
Select top 1
'x' as x,
'y' as y
FROM RealTable
create table dummy as (select '1' col1) with data;
INSERT INTO Student
(Name, Maths, Science, English)
SELECT 'Tilak', 90, 40, 60 from dummy union
SELECT 'Raj', 30, 20, 10 from dummy
;
yes you can try this.
INSERT INTO Student
SELECT (Name, Maths, Science, English) FROM JSON_Table
(ON (SELECT 1 id,cast('{"DataSet" : [
{"s":"m", "Name":"Tilak", "Maths":"90","Science":"40", "English":"60" },
{"s":"m", "Name":"Raj", "Maths":"30","Science":"20", "English":"10" }
]
}' AS json ) jsonCol)
USING rowexpr('$.DataSet[*]')
colexpr('[{"jsonpath":"$.s","type":"CHAR(1)"},{"jsonpath":"$.Name","type":"VARCHAR(30)"}, {"jsonpath":"$.Maths","type":"INTEGER"}, {"jsonpath":"$.Science","type":"INTEGER"}, {"jsonpath":"$.English","type":"INTEGER"}]')
) AS JT(id,State,Name, Maths, Science, English)

Select values that don't occur in a table

I'm sure this has been asked somewhere, but I found it difficult to search for.
If I want to get all records where a column value equals one in a list, I'd use the IN operator.
SELECT idSparePart, SparePartName
FROM tabSparePart
WHERE SparePartName IN (
'1234-2043','1237-8026','1238-1036','1238-1039','1223-5172'
)
Suppose this SELECT returns 4 rows although the list has 5 items. How can I select the value that does not occur in the table?
Thanks in advance.
select t.* from (
select '1234-2043' as sparePartName
union select '1237-8026'
union select '1238-1036'
union select '1238-1039'
union select '1223-5172'
) t
where not exists (
select 1 from tabSparePart p WHERE p.SparePartName = t.sparePartName
)
As soon as you mentioned that i have to create a temp table, i remembered my Split-function.
Sorry for answering my own question, but this might be the the best/simplest way for me:
SELECT PartNames.Item
FROM dbo.Split('1234-2043,1237-8026,1238-1036,1238-1039,1223-5172', ',') AS PartNames
LEFT JOIN tabSparePart ON tabSparePart.SparePartName = PartNames.Item
WHERE idSparePart IS NULL
My Split-function:
Help with a sql search query using a comma delimitted parameter
Thank you all anyway.
Update: I misunderstood the question. I guess in that case I would select the values into a temp table, then select the values which are not in that table. Not ideal, I know -- the problem is that you need to get your list of part names to SQL Server somehow (either via IN or putting them in a temp table) but the semantics of IN don't do what you want.
Something like this:
CREATE TABLE tabSparePart
(
SparePartName nvarchar(50)
)
insert into tabSparePart values('1234-2043')
CREATE TABLE #tempSparePartName
(
SparePartName nvarchar(50)
)
insert into #tempSparePartName values('1234-2043')
insert into #tempSparePartName values('1238-1036')
insert into #tempSparePartName values('1237-8026')
select * from #tempSparePartName
where SparePartName not in (select SparePartName from tabSparePart)
With output:
SparePartName
1238-1036
1237-8026
Original (wrong) answer:
You can just use "not in":
SELECT * from tabSparePart WHERE SparePartName NOT in(
'1234-2043','1237-8026','1238-1036','1238-1039','1223-5172'
)
You could try something like this....
declare #test as table
(
items varchar(50)
)
insert into #test
values('1234-2043')
insert into #test
values('1234-2043')
insert into #test
values('1237-8026')
-- the rest of the values --
select * from #test
where items not in (
select theItemId from SparePartName
)
for fun check this out...
http://blogs.microsoft.co.il/blogs/itai/archive/2009/02/01/t-sql-split-function.aspx
It shows you how to take delimited data and return it from a table valued function as separate "rows"... which my make the process of creating the table to select from easier than inserting into a #table or doing a giant select union subquery.