(A Snowflake user brought this to my attention... Any recommendations?)
After a merge statement, the "result set" can have 1, 2, or 3 columns returned depending on the potential of the merge.
For example,
MERGE ... WHEN MATCHED THEN UPDATE ... will produce only "number of
rows updated"
MERGE ... WHEN MATCHED THEN UPDATE ... WHEN NOT MATCHED THEN INSERT
... will produce both "number of rows updated" and "number of rows
inserted"
MERGE...WHEN MATCHED ... THEN DELETE will also show "number of rows
deleted"
This means that RESULT_SCAN can return different columns depending on the capability of the merge.
Is there a reason why MERGE cannot return all three columns regardless? Afterall, even a simple "UPDATE MyTable SET MyColumn = 5" will return both "number of rows updated" and "number of multi-joined rows updated" even though the capability of the statement would never produce the latter.
Further, is there a list anywhere in Snowflake documentation that shows all the possible columns in the result set for any given operation? I know I could "DESCRIBE RESULT(QueryID)" in order to get the list of column names, but this is added work I was hoping to avoid.
We received this forum response to our original question:
When you are using the result_scan method, are you using the specific
id? If you would like to show all three columns regardless, you may
want to consider using
desc result last_query_id();
select * from table(result_scan(last_query_id()));
reference: https://docs.snowflake.net/manuals/sql-reference/functions/result_scan.html
...shows all the possible columns in the result set for any given
operation?
Are you in the UI, you will be able to see the column names in the
Object Browser:
https://docs.snowflake.net/manuals/user-guide/snowflake-manager.html#worksheet-page
HOWEVER:
If the MERGE doesn't have a path that uses INSERT, then the result set / RESULT_SCAN does not return a "number of rows inserted" column. To me that's a problem. Likewise for paths to an UPDATE or DELETE.
I know how to get all columns for a table. My question is how to get all columns for a given query via RESULT_SCAN. Since it is dynamic and depends on the query id, can you please list all possible columns from a INSERT statement, a MERGE statement, a DESCRIBE statement, etc... ?
Related
I have a column workId in my table which has values like :
W1/2009/12345, G2/2018/2345
Now a user want to get this particular id G2/2018/2345. I am using like operator in my query as below:
select * from u_table as s where s.workId like '%2345%' .
It is giving me both above mentioned workids. I tried following query:
select * from u_table as s where s.workId like '%2345%' and s.workId not like '_2345'
This query also giving me same result.
If anyone please provide me with the correct query. Thanks!
Why not use the existing delimiters to match with your criteria?
select *
from u_table
where concat('/', workId, '/') like concat('%/', '2345', '/%');
Ideally of course your 3 separate values would be 3 separate columns; delimiting multiple values in a single column goes against first-normal form and prevents the optimizer from performing an efficient index seek, forcing a scan of all rows every time, hurting performance and concurrency.
I am trying to find a way to return values from a table like the bottom table:
The tables are provided externally and my understanding is that I can't run filters or sorts as the table is full of extra irrelevant data that would not sort properly across all columns.
I'm approaching this twofold:
firstly, I wanted to return the row information for any entry in the table that has a CPT matching the lookup value.
Second, (where I'm stuck)--when the lookup returns a DESC that corresponds to a matched CPT, the goal would be to also pull in any Code A/Code B entries that correspond to that DESC value.
I found an existing formula that worked for the first part, shown below. (apologies for formatting--SO keeps flagging my draft as having unformatted code).
IFERROR(INDEX($B$3:$B$3000,SMALL(IF(I$2=$A$3:$A$3000,ROW($A$3:$A$3000)- MIN(ROW($A$3:$A$3000))+1,""), ROW()-1)),"")
Currently, the aforementioned formula does return the DESC entries for any matching CPTs and I use vlookup to pull the rest of the relevant columns for a corresponding row.
I'm coming up short in cases where there are multiple Code A/Code B entries for a given DESC, as the lookup only returns information for the row containing the matching CPT and not any relevant codes contained in subsequent rows.
I was thinking I'd have to use something similar to the existing lookup formula to identify a matched row and then display any subsequent rows containing Code entries until the next row with a non-blank CPT entry.
Unfortunately, I don't know if that's actually the best way to approach this. Any resources/suggestions are greatly appreciated.
As always, there will be a reasonable explanation for my surprise, but till then....
I have this query
delete from Photo where hs_id in (select hs_id from HotelSupplier where id = 142)
which executes just fine (later i found out that the entire photo table was empty)
but the strange thing: there is no field hs_id in HotelSupplier, it is called hs_key!
So when i execute the last part
select hs_id from HotelSupplier where id = 142
separately (select that part of the query with the mouse and hit F5), i get an error, but when i use it in the in clause, it doesn't!
I wonder if this is normal behaviour?
It is taking the value of hs_id from the outer query.
It is perfectly valid to have a query that doesn't project any columns from the selected table in its select list.
For example
select 10 from HotelSupplier where id = 142
would return a result set with as many rows as matched the where clause and the value 10 for all rows.
Unqualified column references are resolved from the closest scope outwards so this just gets treated as a correlated sub query.
The result of this query will be to delete all rows from Photo where hs_id is not null as long as HotelSupplier has at least one row where id = 142 (and so the subquery returns at least one row)
It might be a bit clearer if you consider what the effect of this is
delete from Photo where Photo.hs_id in (select Photo.hs_id)
This is of course equivalent to
delete from Photo where Photo.hs_id = Photo.hs_id
By the way this is far and away the most common "bug" that I personally have seen erroneously reported on Microsoft Connect. Erland Sommarskog includes it in his wishlist for SET STRICT_CHECKS ON
It's a strong argument for keeping column names consistent between tables. As #Martin says, the SQL syntax allows column names to be resolved from the outer query, when there's no match in the inner query. This is a boon when writing correlated subqueries, but can trip you up sometimes (as here)
Link to example file:
https://docs.google.com/spreadsheets/d/1dCQSHWjndejkyyw-chJkBjfHgzEGYoRdXmPTNKu7ykg/edit?usp=sharing
The tab "Source data" contains the data to be used in the query on the tab "Query output". The tab "Desired result" shows what I would like the end result to look like.
The goal I'm trying to achieve is to have the formula in cell A2 on the tab "Query output" to populate the data in all four of the columns, so that it looks exactly like the "Desired result" tab. I know I can get the same result simply by entering additional formulas in C2 and D2, but this is not the objective, I need the results to come specifically from the single formula in A2.
The information in the "Additional data 1" column should simply repeat the word "Test" for every row that contains data in the first two columns. The information in the "Additional data 2" column should simply repeat the data from cell 'Source data'!A1 for every row that contains data in the first two columns.
Please feel free to edit the example file as it only contains dummy data. If you like, you can copy the tab "Query output" to create your own working formula for illustrative purposes.
EDIT:
I'm thinking along the lines of creating an array that consists of the required data for the columns "Additional data 1" and "Additional data 2" and then combining that array with the array of the query result which provides the first two columns. I've been experimenting with this in various ways, but so far the only result I have achieved is an error on the first cell of the query results. I also have no idea yet how I could make sure that the second array contains an equal amount of rows to the query result.
You can add static data into query:
=QUERY('Source data'!A3:B,"SELECT A,B, 'Test', '" & 'Source data'!A1 &"' WHERE A IS NOT NULL LABEL A '', B '', 'Test' '', '" & 'Source data'!A1 &"' ''")
Many thanks to #basic for the provided assistance! The insights were a great help to solving my issue. That said, I have muddled along a bit, and I've come up with a slightly different solution which I find better suited as it gives true blank values instead of a column filled with spaces.
First of all, instead of querying directly on the source data, I built an array and queried on that. I used the two existing columns (A and B) from the source data and added a third column to the array which does not exist in the source data. In order to make sure that the third column would consist of blank values, I used the IFERROR formula.
=IFERROR(0/0)
The formula above returns a blank because dividing by zero forces an error and the IFERROR method returns a blank unless an alternative return value is specified.
In order to be able to use this formula in an array however, it had to be tweaked slightly, because as it is it would only return a single blank cell value instead of a column of blank values. To do this, I used an already existing column from the source data, and then encapsulated it in an ARRAYFORMULA.
=ARRAYFORMULA(IFERROR('Source data'!A3:A/0))
Using this, the resulting array has the following formula.
=ARRAYFORMULA({'Source data'!A3:A,'Source data'!B3:B,IFERROR('Source data'!A3:A/0)})
This creates an array consisting of the two original columns A and B from the source data, plus an additional third column filled with blank values. This array can now be queried upon, and using the tricks previously provided by #basic the desired result as specified in the original question can be achieved.
Due to the query now being used upon a user-defined array, the columns in the SELECT statement now have to be referred to as Col1, Col2, Col3, instead of A, B, C. The final formula now looks like this.
=QUERY(ARRAYFORMULA({'Source data'!A3:A,'Source data'!B3:B,IFERROR('Source data'!A3:A/0)}),"SELECT Col1,Col2,'Test',Col3,'"&'Source data'!A1&"' WHERE Col1 IS NOT NULL LABEL 'Test' '','"&'Source data'!A1&"' ''")
I hope this information may prove of use to someone else as well.
I have a view against a OPENQUERY() which gets data from a SSAS cube.
MDX query looks like this:
WITH MEMBER [Measures].[Measure1] AS
(--calculation)
SELECT
{[Measures].[Measure1]}
ON 0,
NON EMPTY ([Dim1].[Dim_key].[Dim_key], [Dim2].[Dim_key].[Dim_key])
ON 1
FROM [Cube]
WHERE ([Dim3].[Hierarchy].[Level].[Member])
My problem is that when the WHERE filter results in 0 rows the view does not work, with error:
Invalid column name '[Dim1].[Dim_key].[Dim_key].[MEMBER_CAPTION]'.
Since its using the column name to have a GROUP BY
How can I force it return at least one row? Or always return the column names?
I cannot remove the NON EMPTY since whole set takes about 1 min to load.
So far I've tried these solutions:
MDX - Always return at least one row even if no data is available
Force mdx query to return column names
but it seems like it does not work since I have a where condition on another dimension.
Managed to figure it out. Added this measure which essentially replicates Dim1 members returned on the rows:
WITH MEMBER [Measures].[Measure1] AS
(--calculation)
MEMBER [Measures].[Dim_Key] AS
[Dim1].[Dim_key].CurrentMember.Member_Key
SELECT
{[Measures].[Measure1]
,[Measures].[Dim_Key]}
ON 0,
NON EMPTY ([Dim1].[Dim_key].[Dim_key], [Dim2].[Dim_key].[Dim_key])
ON 1
FROM [Cube]
WHERE ([Dim3].[Hierarchy].[Level].[Member])
So even if there are no rows I get the new measure back as one of the columns. If there are rows I get two additional columns (which have row tags) but I just don't use them in the view