Snowflake case statement is returning an error instead of the value specified within the ELSE clause - sql

I need to check if one or many fields already exists in a table so I can do a merge into statement using them.
I tried this:
select sat_sector_hkey,
CASE
WHEN EXISTS(select id from hub_sector)
THEN (MERGE INTO ...)
END AS id
from sat_sector;
For testing, I used only one case statement, and replaced merge into with a THEN...ELSE values:
SELECT sat_sector_hkey,
CASE
WHEN EXISTS(select id from hub_sector)
THEN '1'
ELSE ''
END AS id
FROM sat_sector;
When this field does not exists, the query return an error instead of '':
SQL compilation error: error line 3 at position 23 invalid identifier
'ID'
I am using a CASE, because I need to check if a column exists or not, as I don't know if it exists or not due to some technicalities in our data coming from multiple sources.

Try this:
Construct an object with the full row.
Test if the constructed object has data for "ID".
create or replace temp table maybe_id
as
select 1 x, 2 id;
select *,
case
when object_construct(a.*):ID is not null
then '1'
else ''
end as id
from maybe_id a
;
Works for me - it gives 1 when the column id has data, and `` when the column doesn't exist in the table.

Related

SELECT * from a table but add conditional to one column?

Is it possible in PostgreSQL to SELECT * from a table, but add a condition to one column in that result and overwrite it? I'll explain easier with a code example of what I'm trying to do (pseudo code)
SELECT
*,
CASE
WHEN column_name=1 THEN 'one'
WHEN column_name=2 THEN 'two'
ELSE 'other'
END AS column_name
FROM table
and this returns something like:
id | name | column_name | created_at
------------------------------------
1 | Title | one | 123456789
So basically, I want to get every column without having to type each column out, but specifically alter the value of one column in the result based on some condition.
=== UPDATE ======
A little more clarification on what I am doing.
I'm writing a plpgsql function that returns a type of, for the above example RETURNS schema.table. This is then (via Postgraphile) accessed through a GraphQL endpoint and returned to our app, that is all typed with TypeScript using codegen.
So in essence, the column name needs to be 1. the same name and 2. not an alias name, as Postgraphile/GraphQL won't know this value so will be omitted.
=== UPDATE 2 ======
Ok I have done it now, but a different way. I looked at it and realised there is a easier way for me to do this, and why I never did it in the first place I don't know. I won't mark this resolved though, as my answer doesn't answer this question.
To get around this, I simply return my resultset into a varaible and alter this before returning:
SELECT schema.table.* INTO cached_data
...
IF cached_data.column_name = 'something' THEN
cached_data.column_name = 'something-else';
END IF;
RETURN cached_data;
This works perfectly for my situation.
If you have to use *, specify the table (alias if necessary)
SELECT
t1.*,
CASE
WHEN column_name=1 THEN 'one'
WHEN column_name=2 THEN 'two'
ELSE 'other'
END AS column_name
FROM table t1
This will return all columns from table, plus the new column. If you want to replace that column from table, explicitly state all required columns.
Note: If column_name is already a column in the table, then you will get two columns in the result set with the same name using this approach (HT #Milney)
You can give a nickname to your table and use TABLE_ NICKNAME.* as follow:
SELECT t.*,
CASE id
WHEN 1 THEN 'one'
WHEN 2 THEN 'two'
ELSE 'other'
END AS column_name
FROM your_table t

SQL use intermediate results

I have a column with numbers (float) that I would like to categorize and store a category as integer and as label (string). For now assume that the category is simply defined by the FLOOR(x).
This works:
SELECT salary,
FLOOR(salary) AS category_integer,
CASE WHEN FLOOR(salary) = 0
THEN 'foo'
ELSE 'bar'
END AS category_label
FROM test01
but I was wondering if I could use the intermediate variable 'category_integer' defined in the beginning of my query in a later part, something like this:
SELECT salary,
FLOOR(salary) AS category_integer,
CASE WHEN category_integer = 0
THEN 'foo'
ELSE 'bar'
END AS category_label
FROM test01
but this is apparently not how SQL works. I've looked into Common table Expressions but got lost there. Is there a way to reuse intermediate variables in an SQL expression?
SQL Fiddle
I must have missed this but I couldn't find related questions so far.
You may resort to common table expressions - basically a query that produces a labelled result set you can refer to in subsequent queries.
Adapted to your example:
with cte as (
select salary
, floor(salary) as category_integer
from test01
)
SELECT salary
, category_integer
, CASE WHEN category_integer = 0
THEN 'foo'
ELSE 'bar'
END AS category_label
FROM cte
;
Consult the reference for more details: CTE / WITH in pgSQL 9.6.
See it at work in SQL fiddle.
There are pre- and post-selection operations. For example order by and group by are post-selection instructions, distinct for example filters out duplicate results during the selection proces itself and as such duplicate results do not even enter the result set to be ordered or grouped.
When you use AS, you are telling PostgreSQL to take the result and put it in a column named category_integer in the output. You are not actually making a variable here that's available during query execution, as the result is only available after the query executes. As such, you can only do this with subselects where you have the result available as a virtual table in itself, where category_integer is a column in a table rather than a variable.
SELECT category_integer,
CASE WHEN category_integer = 0
THEN 'foo'
ELSE 'bar'
END AS category_label
FROM (SELECT FLOOR(0) AS category_integer FROM test01) AS test02
https://www.postgresql.org/docs/current/static/queries-select-lists.html
https://www.postgresql.org/docs/current/static/queries-table-expressions.html#QUERIES-TABLE-ALIASES

Display default value if query results in no records in BigQuery

A query can return an empty table on BigQuery. An example of such occurrence is if I join a bunch of tables in a query on BigQuery and the result of the joins is an empty table, or if there are no matches based on the where clause.
Here is a dumb sample query that will always return in an empty join:
#standardSQL
WITH query1 AS (
SELECT 1 AS number, "one" AS message
), query2 AS (
SELECT 2 AS number, "two" AS message)
SELECT "query result" AS result, query1.*
FROM query1
JOIN query2 ON query1.number = query2.number;
The query will show this output: Query returned zero records.
If that's the case I want to return either a message or a default row. But I don't know how to do that. I've tried using IFNULL, but that's only usuable for one column, not number of columns. Using an IF statement gave me errors as you can't return row(s) from an if statement.
I think the error it gave me was Scalar subquery cannot have more than one column unless using SELECT AS STRUCT to build STRUCT values.
Another thing that I could think of, but don't know how to implement is to add a UNION at the end that would only trigger if the previous parts didn't return anything. Or wrap the existing query in a WITH statement subquery and if that returns nothing, print a message, else do SELECT * FROM sub_query.
I'd like to either just display a message when an empty table is the result, or return a row with some default values.
I understand the answer is likely to contain a UNION statement and hence displaying just a message won't be possible. In that case I'd like to display a default row instead. For the above sample query a default row would look like: "No results found", NULL, NULL.
When the query returns a non empty table, I want it to look exactly like it did with the original query. So there shouldn't be any added columns or change to the schema of the result.
You would use union all. Something like this:
with t as (
. . . <all your query stuff here>
)
select cast(NULL as string) as msg, t.*
from t
union all
select msg, t.* -- all the `t` columns will be `NULL`
from (select 'No rows returned' as msg) left join
t
on 1 = 0 -- always false
where not exists (select 1 from t);
Note the complications. A query returns a fixed set of columns with a fixed set of names. This version returns an extra column at the beginning of the data to contain the message. In order to get all the rest of the columns, a left join is used, but the on clause is always false.
Option 1
Below displays row with all nulls in case if there is no result returned for your_query
#standardSQL
WITH your_query AS ( ... )
SELECT * FROM your_query
UNION ALL
SELECT your_query.* REPLACE ("No results found" AS result)
FROM (SELECT 1)
LEFT JOIN your_query ON FALSE
WHERE NOT EXISTS (SELECT 1 FROM your_query)
Row result number message
1 No results found null null
Option 2
If you know in advance output schema - below returns default row (assuming 0 default for number and "none" default for message
#standardSQL
WITH your_query AS ( ... )
SELECT * FROM your_query
UNION ALL
SELECT "No results found", 0, "none" FROM (SELECT 1)
LEFT JOIN your_query ON FALSE
WHERE NOT EXISTS (SELECT 1 FROM your_query)
Row result number message
1 No results found 0 none

Selecting from table to insert into another, getting a type error

I have the following query which inserts data into one table after selecting it from another.
The problem is that the data types do not match for one of the columns. I have simplified the query below.
INSERT INTO tbl.LogTable (
[SelPartNo], -- This does not match, see below
)
SELECT TOP 1
IF([SelPartNo] = 'False', NULL, [SelPartNo],
FROM tbl.MyTable
WHERE ID = '20358'
ORDER BY CreateDate DESC
The first SelPartNo is an int and the second is a VarChar. In most instances the SelPartNo for the second one (tbl.MyTable) is NULL or an integer, which I don't think will cause a problem. But in some cases the value is "False", which needs to return NULL.
I have tried an IF statement but I am doing something wrong because it's giving a syntax error and I am unsure if this is the correct approach.
Your code is syntactically incorect...
Try it with
NULLIF([SelPartNo],'False')
This function returns NULL if the two expressions are equal.
Details: https://msdn.microsoft.com/en-us/library/ms177562.aspx
I don't think IF is a function, at least not one which you can use in a SELECT statement. But CASE WHEN ... END is your friend:
INSERT INTO tbl.LogTable (
[SelPartNo]
)
SELECT TOP 1
CASE WHEN [SelPartNo] = 'False' THEN NULL ELSE [SelPartNo] END
FROM tbl.MyTable
WHERE ID = '20358'
ORDER BY CreateDate DESC

Postgresql case and testing boolean fields

First: I'm running postgresql 8.2 and testing my queries on pgAdmin.
I have a table with some fields, say:
mytable(
id integer,
mycheck boolean,
someText varchar(200));
Now, I want a query similary to this:
select id,
case when mycheck then (select name from tableA)
else (select name from tableB) end as mySpecialName,
someText;
I tried to run and get this:
ERROR: CASE types character varying and boolean cannot be matched
SQL state: 42804
And even trying to fool postgresql with
case (mycheck::integer) when 0 then
didn't work.
So, my question is: since sql doesn't have if, only case, how I'm suppose to do an if with a boolean field?
Your problem is a mismatch in your values (expressions after then and else), not your predicate (expression after when). Make sure that select name from tableA and select name from tableB return the same result type. mycheck is supposed to be a boolean.
I ran this query on PostgreSQL 9.0beta2, and (except for having to add from mytable to the SELECT statement as well as creating tables tableA and tableB), and it didn't yield any type errors. However, I get an error message much like the one you described when I run the following:
select case when true
then 1
else 'hello'::text
end;
The above yields:
ERROR: CASE types text and integer cannot be matched
I just ran this fine on PostgreSQL 8:
select id,
case when mycheck = true then (...)
else (...),
someText;