Using values inside a cte in snowflake queries - sql

I'm trying to use values inside a cte in snowflake. For example, this:
WITH person (id, name)
AS (VALUES (1, 'Ted'), (2, 'Bill'))
SELECT *
FROM person;
works fine in (for example) postgres, but gives me the following error in snowflake:
SQL compilation error: syntax error line 2 at position 9 unexpected 'VALUES'. syntax error line 2 at position 17 unexpected '1'. syntax error line 2 at position 26 unexpected ','.
However, from snowflake documentation I can see VALUES syntax is supported, for example this works in snowflake:
SELECT * FROM (VALUES (1, 'Ted'), (2, 'Bill')) as person (id, name);
So I'm wondering how I'd get it to work inside a cte.
I would like the same output as I would get from postgres (using a cte)
id | name
----+------
1 | Ted
2 | Bill
(2 rows)

You were close
with person (id, name) as
(select * from values (1, 'ted'), (2, 'bill'))
select *
from person;

You have to add a SELECT statement inside your CTE
WITH person AS (
SELECT $1 AS id,
$2 AS name
FROM (VALUES (1, 'Ted'), (2, 'Bill'))
)
SELECT *
FROM person;

You can define table and column alias like this:
select * from (values (1, 'One'), (3, 'three')) as person (ID, NAME);
And then of course reference the table expression in a CTE:
WITH PERSON as
(
select * from (values (1, 'One'), (3, 'three')) as person (ID, NAME)
)
SELECT * FROM person;

Related

sql insert multiple rows from another table

I have tables:
ContainerSubTypes (Id, Name) with many records,
WorkAreas (Id, Name) with only one row,
WorkAreaContainerSubTypes table (Id, WorkAreaId, ContainerSubTypeId) - empty table.
I need to insert data into WorkAreaContainerSubTypes table, it should look like:
(1, 1, containersubtype1_id)
(2, 1, containersubtype2_id)
(3, 1, containersubtype3_id)
And I'm trying sql like:
INSERT INTO WorkAreaContainerSubTypes (WorkAreaId, ContainerSubTypeId)
VALUES
(1, (SELECT Id FROM ContainerSubTypes));
But SELECT query has more than 1 row and it throws error.
How can I implement that inserting?
VALUES table value constructor specifies a set of row value expressions to be constructed into a table, so you need a different statement:
INSERT INTO WorkAreaContainerSubTypes (WorkAreaId, ContainerSubTypeId)
SELECT 1, Id
FROM ContainerSubTypes;
As an additional note, you may use VALUES only if SELECT Id FROM ContainerSubTypes statement returns a single row or no rows (perhaps with an optional WHERE clause):
INSERT INTO WorkAreaContainerSubTypes (WorkAreaId, ContainerSubTypeId)
VALUES
(1, (SELECT Id FROM ContainerSubTypes WHERE Name = 'Some name'));
Just use insert . . . select:
INSERT INTO WorkAreaContainerSubTypes (WorkAreaId, ContainerSubTypeId)
SELECT 1, Id
FROM ContainerSubTypes;

Alternative to NOT IN in SSMS

I have my table in this structure. I am trying to find all the unique ID's whose word's do not appear in the list. How can I achieve this in MS SQL Server.
id word
1 hello
2 friends
2 world
3 cat
3 dog
2 country
1 phone
4 eyes
I have a list of words
**List**
phone
eyes
hair
body
Expected Output
Except the words from the list, I need all the unique ID's. In this case it is,
2
3
I & 4 is not in the output as their words appears in the List
I tried the below code
Select count(distinct ID)
from Table1
where word not in ('phone','eyes','hair','body')
I tried Not Exists also which did not work
You can also use GROUP BY
SELECT id
FROM Table1
GROUP BY id
HAVING MAX(CASE WHEN word IN('phone', 'eyes', 'hair', 'body') THEN 1 ELSE 0 END) = 0
One way to do it is to use not exists, where the inner query is linked to the outer query by id and is filtered by the search words.
First, create and populate sample table (Please save us this step in your future questions):
DECLARE #T AS TABLE (
id int,
word varchar(20)
)
INSERT INTO #T VALUES
(1, 'hello'),
(2, 'friends'),
(2, 'world'),
(3, 'cat'),
(3, 'dog'),
(2, 'country'),
(1, 'phone'),
(4, 'eyes')
The query:
SELECT DISTINCT id
FROM #T t0
WHERE NOT EXISTS
(
SELECT 1
FROM #T t1
WHERE word IN('phone', 'eyes', 'hair', 'body')
AND t0.Id = t1.Id
)
Result:
id
2
3
SELECT t.id FROM dbo.table AS t
WHERE NOT EXISTS (SELECT 1 FROM dbo.table AS t2
INNER JOIN
(VALUES('phone'),('eyes'),('hair'),('body')) AS lw(word)
ON t2.word = lw.word
AND t2.id = t.id)
GROUP BY t.id;
You can try this as well: this is a dynamic table structure:
DECLARE #T AS TABLE (id int, word varchar(20))
INSERT INTO #T VALUES
(1, 'hello'),
(2, 'friends'),
(2, 'world'),
(3, 'cat'),
(3, 'dog'),
(2, 'country'),
(1, 'phone'),
(4, 'eyes')
DECLARE #tblNotUsed AS TABLE ( id int, word varchar(20))
DECLARE #tblNotUsedIds AS TABLE (id int)
INSERT INTO #tblNotUsed VALUES
(1, 'phone'),
(2, 'eyes'),
(3, 'hair'),
(4, 'body')
INSERT INTO #tblNotUsedIds (id)
SELECT [#T].id FROM #T INNER JOIN #tblNotUsed ON [#tblNotUsed].word = [#T].word
SELECT DISTINCT id FROM #T
WHERE id NOT IN (SELECT id FROM #tblNotUsedIds)
The nice thing about SQL is there are sometimes many ways to do things. Here is one way is to place your list of known values into a #temp table and then run something like this.
Select * from dbo.maintable
EXCEPT
Select * from #tempExcludeValues
The results will give you all records that aren't in your predefined list. A second way is to do the join like Larnu has mentioned in the comment above. NOT IN is typically not the fastest way to do things on larger datasets. JOINs are by far the most efficient method of filtering data. Many times better than using a IN or NOT IN clause.

oracle correlated subquery using distinct listagg

I have an interesting query I'm trying to figure out. I have a view which is getting a column added to it. This column is pivoted data coming from other tables, to form into a single row. Now, I need to wipe out duplicate entries in this pivoted data. Listagg is great for getting the data to a single row, but I need to make it unique. While I know how to make it unique, I'm tripping up on the fact that correlated sub-queries only go 1 level deep. So... not really sure how to get a distinct list of values. I can get it to work if I don't do the distinct just fine. Anyone out there able to work some SQL magic?
Sample data:
drop table test;
drop table test_widget;
create table test (id number, description Varchar2(20));
create table test_widget (widget_id number, test_fk number, widget_type varchar2(20));
insert into test values(1, 'cog');
insert into test values(2, 'wheel');
insert into test values(3, 'spring');
insert into test_widget values(1, 1, 'A');
insert into test_widget values(2, 1, 'A');
insert into test_widget values(3, 1, 'B');
insert into test_widget values(4, 1, 'A');
insert into test_widget values(5, 2, 'C');
insert into test_widget values(6, 2, 'C');
insert into test_widget values(7, 2, 'B');
insert into test_widget values(8, 3, 'A');
insert into test_widget values(9, 3, 'C');
insert into test_widget values(10, 3, 'B');
insert into test_widget values(11, 3, 'B');
insert into test_widget values(12, 3, 'A');
commit;
Here is an example of the query that works, but shows duplicate data:
SELECT A.ID
, A.DESCRIPTION
, (SELECT LISTAGG (WIDGET_TYPE, ', ') WITHIN GROUP (ORDER BY WIDGET_TYPE)
FROM TEST_WIDGET
WHERE TEST_FK = A.ID) widget_types
FROM TEST A
Here is an example of what does NOT work due to the depth of where I try to reference the ID:
SELECT A.ID
, A.DESCRIPTION
, (SELECT LISTAGG (WIDGET_TYPE, ', ') WITHIN GROUP (ORDER BY WIDGET_TYPE)
FROM (SELECT DISTINCT WIDGET_TYPE
FROM TEST_WIDGET
WHERE TEST_FK = A.ID))
WIDGET_TYPES
FROM TEST A
Here is what I want displayed:
1 cog A, B
2 wheel B, C
3 spring A, B, C
If anyone knows off the top of their head, that would fantastic! Otherwise, I can post up some sample create statements to help you with dummy data to figure out the query.
You can apply the distinct in a subquery, which also has the join - avoiding the level issue:
SELECT ID
, DESCRIPTION
, LISTAGG (WIDGET_TYPE, ', ')
WITHIN GROUP (ORDER BY WIDGET_TYPE) AS widget_types
FROM (
SELECT DISTINCT A.ID, A.DESCRIPTION, B.WIDGET_TYPE
FROM TEST A
JOIN TEST_WIDGET B
ON B.TEST_FK = A.ID
)
GROUP BY ID, DESCRIPTION
ORDER BY ID;
ID DESCRIPTION WIDGET_TYPES
---------- -------------------- --------------------
1 cog A, B
2 wheel B, C
3 spring A, B, C
I was in a unique situation using the Pentaho reports writer and some inconsistent data. The Pentaho writer uses Oracle to query data, but has limitations. The data pieces were unique but not classified in a consistent manner, so I created a nested listagg inside of a left join to present the data the way I wanted to:
left join
(
select staff_id, listagg(thisThing, ' --- '||chr(10) ) within group (order by this) as SCHED_1 from
(
SELECT
staff_id, RPT_STAFF_SHIFTS.ORGANIZATION||': '||listagg(
RPT_STAFF_SHIFTS.DAYS_OF_WEEK
, ',' ) within group (order by BEGIN_DATE desc)
as thisThing
FROM "RPT_STAFF_SHIFTS" where "RPT_STAFF_SHIFTS"."END_DATE" is null
group by staff_id, organization)
group by staff_id
) schedule_1 on schedule_1.staff_id = "RPT_STAFF"."STAFF_ID"
where "RPT_STAFF"."STAFF_ID" ='555555'
This is a different approach than using the nested query, but it some situations it might work better by taking into account the level issue when developing the query and taking an extra step to fully concatenate the results.

How to SELECT by an array in postgresql?

Say I have an table like this:
DROP TABLE tmp;
CREATE TABLE tmp (id SERIAL, name TEXT);
INSERT INTO tmp VALUES (1, 'one'), (2, 'two'), (3, 'three'), (4, 'four'), (5, 'five');
SELECT id, name FROM tmp;
It's like this:
id | name
----+-------
1 | one
2 | two
3 | three
4 | four
5 | five
(5 rows)
Then I have an array of ARRAY[3,1,2]. I want to get query the table by this array, so I can get an array of ARRAY['three', 'one', 'two']. I think this should be very easy but I just can't get it figured out.
Thanks in advance.
To preserve the array order, it needs to be unnested with the index order (using row_number()), then joined to the tmp table:
SELECT array_agg(name ORDER BY f.ord)
FROM (
select row_number() over() as ord, a
FROM unnest(ARRAY[3, 1, 2]) AS a
) AS f
JOIN tmp ON tmp.id = f.a;
array_agg
-----------------
{three,one,two}
(1 row)
Use unnest function:
SELECT id, name FROM tmp
WHERE id IN (SELECT unnest(your_array));
There is a different technique as suggested by Eelke:
You can also use the any operator
SELECT id, name FROM tmp WHERE id = ANY ARRAY[3, 1, 2];
If you want to return the array as output then try this:
SELECT array_agg(name) FROM tmp WHERE id = ANY (ARRAY[3, 1, 2]);
SQL FIDDLE

How to get value without subqueries (on SQL-Server)?

I have the following table on SQL Server:
ID Num
1 A
2 B
2 B
3 C
3 C
4 C
(Num is a numeric column - A, B, and C are standins for numeric values, for the purpose of this question)
How to get the value of A+B+C+C without using subqueries and CTE?
A - for 1, B - for 2, C - for 3, C - for 4.
The answer seems to sum(distinct Num), but distinct is by ID field!
Demo table:
create table test (ID int, Num int);
insert into test values (1, 10);
insert into test values (2, 100);
insert into test values (2, 100);
insert into test values (3, 1000);
insert into test values (3, 1000);
insert into test values (4, 1000);
The correct answer is 10+100+1000+1000 = 2110.
A random guess, using CTE to avoid the pointless subquery restriction:
With X as (Select Distinct Id, No From Test)
Select
Sum(No)
From X
Or using a derived table (which works in SQL 2000):
Select
Sum(No)
From (
Select Distinct
Id,
No
From
Test
) a;
http://sqlfiddle.com/#!3/77a6e/6
The solution:
select cast(sum(distinct Num + cast(0.00001 as number(38,19))/ID) as number(18,2))