create table as select with primary key in snowflake --need syntax - sql

I know it works (when there is no select as)
CREATE TABLE t1 ( dt1 date ,primary key (dt1)) ;
I also know alter table works like below:
CREATE TABLE t2 as (select current_date as dt1 ) ;
alter TABLE t2 add primary key (dt1) ;
But I need a syntax for create table with PK when there is SELECT in Create Table. I am trying to convert existing code and other DB support Create Table as select with PK.

You can do it like you'd normally add a primary key:
create or replace transient table test_table (number_1 number primary key, number_2 number, number_3 number) as (
select column1, column2, column3
from
values (1, 2, 3),
(3, 4, 5)
)
;
Results
describe table TEST_TABLE;
-- +--------+------------+------+-----+-------+-----------+----------+
-- |name |type |kind |null?|default|primary key|unique key|
-- +--------+------------+------+-----+-------+-----------+----------+
-- |NUMBER_1|NUMBER(38,0)|COLUMN|N |NULL |Y |N | --> Primary Key
-- |NUMBER_2|NUMBER(38,0)|COLUMN|Y |NULL |N |N |
-- |NUMBER_3|NUMBER(38,0)|COLUMN|Y |NULL |N |N |
-- +--------+------------+------+-----+-------+-----------+----------+

In the documentation for Variant Create Table syntax it looks like the following should work:
CREATE TABLE t1 ( dt1 Date, primary key (dt1))
AS SELECT CURRENT_DATE;

Related

How to create post-order traversal in SQL?

Having a tree structure in database (id, parentId, order) where order means some value to sort per parent (it means order in parent list), how one can build full-SQL query to traverse this table in POST-ORDER?
What is post order is described on wiki, though only for binary trees - https://en.wikipedia.org/wiki/Tree_traversal
Not all of them is applyable to custom tree (for example IN-ORDER), but POST-ORDER is actully applyable:
A
/ \
B C
/|\
D E F
output will be:
B D E F C A
The same in SQL data table:
|Id |ParentId | Order
|___|_________|______
|A |null |0
|B |A |0
|C |A |1
|D |C |0
|E |C |1
|F |C |2
I have been struggling with it quite a time, but looks like CTE doesn't allow inner ORDER BY clause (omg, why?!), so this task becomes impossible at my current level without stored procedures.
More as a proof of concept than a usable answer, here's a CTE-based version. It uses STRING_AGG to concatenate the children of each node in order, then recursively replaces each node with its children to build the output string - this means it wouldn't work in situations where node keys are substrings of one another.
DECLARE #t TABLE
(id CHAR(1) NOT NULL PRIMARY KEY,
parentid CHAR(1) NULL,
roworder TINYINT NOT NULL
)
INSERT #t (id, parentid, roworder)
VALUES('A', NULL, 0),
('B','A',0),
('C','A',1),
('D','C',0),
('E','C',1),
('F','C',2),
('G','E',0),-- two extra nodes to prove that this isn't a one-off
('H','E',1)
;WITH aggCTE
AS
(
SELECT parentid, STRING_AGG(CONVERT(VARCHAR(MAX), id), ' ') WITHIN GROUP (ORDER BY Roworder) AS children
FROM #t
GROUP BY parentid
)
,recCTE
AS
(
SELECT a.parentid,
a.children,
CAST(ISNULL(a.parentid,'') AS VARCHAR(MAX)) AS processed, --to prevent loops
0 AS seq, --to pick the right output row
a.children AS firstnode --to disambiguate if the data contains multiple trees
FROM aggCTE AS a
WHERE a.parentid IS NULL
UNION ALL
SELECT a.parentid,
REPLACE(a.children, b.parentid, CONCAT(b.children, ' ', b.parentid)) AS children,
CONCAT(a.processed, b.parentid) AS processed,
a.seq + 1 AS seq,
a.firstnode
FROM recCTE AS a
JOIN aggCTE AS b
ON CHARINDEX(b.parentid, a.children) > 0
AND CHARINDEX(b.parentid, a.processed) = 0
)
,rnCTE
AS
(
SELECT children,
ROW_NUMBER() OVER (PARTITION BY firstnode ORDER BY seq DESC) AS rn
FROM recCTE
)
SELECT children AS post_order_traversal
FROM rnCTE
WHERE rn = 1
Found solution by simply implementing it as is:
If(OBJECT_ID('tempdb..#result') Is Not Null) Drop Table #result;
If(OBJECT_ID('tempdb..#stack') Is Not Null) Drop Table #stack;
create table #result (id int not null identity primary key, [value] bigint null);
create table #stack (id int not null identity primary key, [value] bigint null);
INSERT INTO #stack values(null) --inserting root identifiers here
WHILE EXISTS (SELECT * FROM #stack)
BEGIN
declare #stack_id int, #stack_value bigint
select top 1 #stack_id=id, #stack_value=value from #stack order by id desc
delete from #stack where id=#stack_id
INSERT INTO #stack
-- here comes our query, which should fetch children of specified id and order it
select tos.id
from inputTable as t
where (ISNULL(#stack_value, 0) = 0 AND t.ParentId IS NULL) OR (ISNULL(#stack_value, 0) != 0 AND t.ParentId = #stack_value)
order by t.[order] asc
insert into #result values(#stack_value)
END
select [value] from #result order by id desc
If(OBJECT_ID('tempdb..#result') Is Not Null) Drop Table #result;
If(OBJECT_ID('tempdb..#stack') Is Not Null) Drop Table #stack;
As of now, it seems impossible with usage of CTE.

Value from 2nd Row of column copied to 1st row of next column

The table looks like below.
DROP TABLE #TEMP
CREATE TABLE #TEMP
(
UVRID VARCHAR(20),
DynamoNo INT,
FREQHZ INT
)
INSERT #TEMP
SELECT '15AL78',100,10 UNION ALL
SELECT '15AL78',110,20 UNION ALL
SELECT '257T13',100,10 UNION ALL
SELECT '257T13',110,20 UNION ALL
SELECT '257T13',931,30
I am trying to make 1 new column say SuprerFrez whose value is depends on column FREQHZ.
For every UVRID group 2nd value FREQHZ will be 1st value of SuprerFrez
and for last FREQHZ, SuprerFrez value will be zero.
Expected output with 1 new column whose value depends upon FREQHZ column. Order by FREQHZ ASC
UVRID |DynamoNo|FREQHZ|SuprerFrez
'15AL78'|100 |10 |20
'15AL78'|110 |20 |0
'257T13'|100 |10 |20
'257T13'|110 |20 |30
'257T13'|931 |30 |0
You are looking for lead():
select t.*,
lead(FREQhz, 1, 0) over (partition by UVRID order by DynamoNo) as SuprerFrez
from #temp t;
Note this assumes that the ordering is by DynamoNo. If that is not the ordering you have in mind, then you need another column that specifies the ordering. For instance, if you wanted "insert" order, you could use an identity column:
CREATE TABLE #TEMP (
TempID INT IDENTITY(1, 1) PRIMARY KEY,
UVRID VARCHAR(20),
DynamoNo INT,
FREQHZ INT
);
Then the code would look like:
select t.*,
lead(FREQhz, 1, 0) over (partition by UVRID order by TempID) as SuprerFrez
from #temp t;

How to manually populate null values without using the 'identity' command in an SQL table

I created the table 'test':
create table test
(
column1 varchar(10),
column2 varchar(10),
)
and added the values
insert into test values('value1','value2')
insert into test values('value1','value2')
But now I need to create a column that will be a primary key, but I can not use the 'Identity' command because the control will be done by the application.
alter table test add ID int
How do I populate values that are null so they stay in sequence? Where as they are null.
result from 'select * from test':
column1 column2 ID
value1 value2 NULL
value1 value2 NULL
Try this
;WITH cte
AS
(
SELECT *, ROW_NUMBER() OVER(ORDER BY column1, column2) AS RowNum FROM test
)
UPDATE cte
SET iID = RowNum
Now check your table records
SELECT * FROM test
You can do this:
add a nullable column Id
update Id with a value
set Id as not null
make Id the primary key.
create table test (
column1 varchar(10)
, column2 varchar(10)
);
insert into test values
('value1','value2')
,('value1','value2');
alter table test add Id int null;
update t set Id = rn
from (
select *
, rn = row_number() over (order by column1, column2)
from test
) as t;
alter table test alter column Id int not null;
alter table test
add constraint pk_test primary key clustered (Id);
select * from test;
test setup: http://rextester.com/DCB57058
results:
+---------+---------+----+
| column1 | column2 | Id |
+---------+---------+----+
| value1 | value2 | 1 |
| value1 | value2 | 2 |
+---------+---------+----+
Add temp identity column, copy values from this column to ID, then drop temp column:
CREATE TABLE #test
(
column1 varchar(10),
column2 varchar(10),
)
INSERT INTO #test
VALUES
('aa','aa'),
('bb','bb')
ALTER TABLE #test
ADD ID INT
ALTER TABLE #test
ADD TempID INT IDENTITY(1,1)
UPDATE t
SET
t.ID = t.TempID
FROM #test t
ALTER TABLE #test
DROP COLUMN TempID
SELECT *
FROM #test t
DROP TABLE #test
In the first place, you can't create a PK with NULL values (check MSDN here).
If you want to make your application to create your PK value, you have to give it at INSERT time or give some "Default" value(s) before your application edit it. The second option is dangerous for various reasons (trust your application to ensure unicity? how will you make a lot of INSERT in a short time? etc).

Insert multiple rows with one column from another table

I have two tables
CREATE TABLE table1 (
id bigint NOT NULL,
name character varying(255),
CONSTRAINT table1_pkey PRIMARY KEY (id)
);
CREATE TABLE table2 (
id bigint NOT NULL,
name character varying(255),
table1_id bigint,
CONSTRAINT table2_pkey PRIMARY KEY (id),
CONSTRAINT fk_table1_table2 FOREIGN KEY (table1_id)
REFERENCES table1 (id) MATCH SIMPLE
);
now what i want to do is for each entry in table1 add entry in table2
ie if my table 1 has entries
|id | name |
|1 | First |
|2 | Second |
|3 | Third |
I need to create three entries in table2
insert into table2 (id,name,table2_id) values (nextval('table2_seq'),'new entry', 1);
insert into table2 (id,name,table2_id) values (nextval('table2_seq'),'new entry', 2);
insert into table2 (id,name,table2_id) values (nextval('table2_seq'),'new entry', 3);
and as only foreign key is changed for each new entry, i was wonder is it any possibility to automate this process.
Is it possible to achieve with query, or should i look at procedures?
Use an insert based on a select:
insert into table2 (id,name,table1_id)
select nextval('table2_seq'), 'new entry', t1.id
from table1;
I struggled to get a "SELECT ... WHERE" in the subquery, likely not enough coffee, but finalised on the below syntax:
insert into table (col1, col2 ...)
select 'staticval1',
r.variable1 from reftable r where r.variable2 = 'Some search term'
...;

Is there a way to set AUTO_INCREMENT property on existing table column in Vertica?

Suppose I have a simple table:
CREATE TABLE user(
id INT NOT NULL PRIMARY KEY,
name VARCHAR(32) NOT NULL,
)
Is there a way to alter this table so id will become AUTO_INCREMENT field?
I tried the following with no luck:
ALTER TABLE (no such syntax)
Creating another table with auto increment ID, and copying the data from the original one (didn't work because of the error: Cannot insert into or update IDENTITY/AUTO_INCREMENT column "id")
Thanks!
I would try to just rank the rows, and use the sequence for future inserts.
\set AUTOCOMMIT 'on'
CREATE TABLE t1 (
val char(1)
);
INSERT INTO t1 VALUES ('a');
INSERT INTO t1 VALUES ('b');
INSERT INTO t1 VALUES ('c');
INSERT INTO t1 VALUES ('d');
CREATE TABLE t2 (
id int,
val char(1)
);
INSERT INTO t2 (val, id)
SELECT val, RANK() OVER (ORDER BY val) as id
FROM t1;
SELECT * FROM t2;
We get:
id | val
----+-----
1 | a
3 | c
2 | b
4 | d
Success!
Let's prepare the table for future inserts:
-- get the value to start sequence at
SELECT MAX(id) FROM t2;
-- create the sequence
CREATE SEQUENCE seq1 START 5;
-- syntax as of 6.1
-- modify the column to add next value for future rows
ALTER TABLE t2 ALTER COLUMN id SET DEFAULT NEXTVAL('seq1');
Quick test:
INSERT INTO t2 (val) VALUES ('e');
INSERT INTO t2 (val) VALUES ('f');
SELECT * FROM t2;
We get:
id | val
----+-----
4 | d
2 | b
3 | c
6 | f
1 | a
5 | e
Hope this helps.