I have a variant data type that I am performing a lateral flatten on but I then need to left join one of the json elements to lookup the value for the corresponding ID from another relational table within Snowflake. When I do this it gives me the error "Lateral View cannot be on the left side of join" which doesn't make sense because if I don't include the outer join an create the view and then create an additional view on top of this view, it allows me to perform a left join.
Example:
create or replace view my_view
copy grants
as
select
rowid as row_id,
siteData.value:siteID::int as site_id,
es.site_name AS site_name
from
"RAW_DATA" raw,
lateral flatten(datamap:data, outer => true) siteData
LEFT join ext_site es on es.siteid = siteData.value:siteID
;
I cannot explain the limitation on not being able to LEFT JOIN after a LATERAL, but here are two potential workarounds to the issue...
Option 1 - Use a CTE (Common Table Expression)
create or replace view my_view
copy grants
as
with my_cte as (
select
rowid as row_id,
siteData.value:siteID::int as site_id
from
"RAW_DATA" raw,
lateral flatten(datamap:data, outer => true) siteData
)
select
c.row_id,
c.site_id,
es.site_name
from
my_cte c
LEFT join ext_site es on es.siteid = c.site_id
;
Option 2 - Use an inline (anonymous) view
create or replace view my_view
copy grants
as
select
c.row_id,
c.site_id,
es.site_name
from
(
select
rowid as row_id,
siteData.value:siteID::int as site_id
from
"RAW_DATA" raw,
lateral flatten(datamap:data, outer => true) siteData
) c
LEFT join ext_site es on es.siteid = c.site_id
;
Personally, I would use the CTE approach, as I find it much more readable.
Regarding your use of outer => true, that is required only if the VARIANT structure is somehow inconsistent and does not guarantee the existence of a data key within your datamap dictionary having a value (array or dictionary) that itself contains one or more elements or attributes. If it is not required, then I would remove it.
Try this workaround:
create or replace view my_view
copy grants
as
select
rowid as row_id,
siteData.value:siteID::int as site_id,
es.site_name AS site_name
from
"RAW_DATA" raw
LEFT join table(flatten(input => raw.datamap:data, outer => TRUE)) siteData
LEFT join ext_site es on es.siteid = siteData.value:siteID::int
;
Related
We have three tables: "USERS", "COMPANIES" and "FILES", where each USER has a variant column named: "COMAPNY_IDS" that is a simple array that matches records in the "COMPANIES" table.
Each FILE belongs to a single COMPANY via its "COMPANY_ID" field.
We would like to join each USER with every one of the FILEs that its connected to via any of the COMPANIES its associated to.
This naive solution:
SELECT u._id as user_id, z._id as file_id, z.SENT_ON
FROM users u,
LATERAL (SELECT f._id, f.SENT_ON
from FILES f
where ( ARRAY_CONTAINS(TO_VARIANT(f.COMPANY_ID), TO_ARRAY(u.COMPANY_IDS)))) z
Takes forever and never finishes.
A more convoluted solution that avoids the "ARRAY_CONTAINS" function - finishes in a second and a half:
SELECT u._id as user_id, max(files.sent_on)
FROM users u
LEFT JOIN (
select *
FROM companies c,
lateral (select flt.value as cid , us._id as uid
from users us,
lateral flatten ( company_ids) as flt
where cid = c._id) ccc
) x on (x.uid = u._id)
LEFT JOIN files
on x.cid = files.company_id
GROUP BY u._id
Is there something preventing ARRAY_CONTAINS from being used properly in "ON" clauses or as part of the "WHERE" clause of correlated lateral sub-queries?
I have two tables
1- Table of TestModules
TestModules
2- Table of TestModule_Results
TestModule_Results
in order to get the required information for each TestModule, I am using FULL OUTER JOIN and it works fine.
FULL OUTER JOIN result
But what is required is slightly different. The above picture shows that TestModuleID = 5 is listed twice, and the requirement is to list the 'up-to-date' results based on time 'ChangedAt'
Of course, I can do the following:
SELECT TOP 1 * FROM TestModule_Results
WHERE DeviceID = 'xxx' and TestModuleID = 'yyy'
ORDER BY ChangedAt DESC
But this solution is for a single row and I want to do it in a Stored Procedure.
Expected output should be like:
ExpectedOutput
Any advise how can I implement it in a SP?
Use a Common Table Expression and Row_Number to add a field identifying the newest results, if any, and select for just those
--NOTE: a Common Table Expression requires the previous command
--to be explicitly terminiated, prepending a ; covers that
;WITH cteTR as (
SELECT *
, ROW_NUMBER() OVER (PARTITION BY DeviceID, TestModuleID
ORDER BY ChangedAt DESC) AS ResultOrder
FROM TestModule_Results
--cteTR is now just like TestModule_Results but has an
--additional field ResultOrder that is 1 for the newest,
--2 for the second newest, etc. for every unique (DeviceID,TestModuleID) pair
)
SELECT *
FROM TestModules as M --Use INNER JOIN to get only modules with results,
--or LEFT OUTER JOIN to include modules without any results yet
INNER JOIN cteTR as R
ON M.DeviceID = R.DeviceID AND M.TestModuleID = R.TestModuleID
WHERE R.ResultOrder = 1
-- OR R.ResultOrder IS NULL --add if Left Outer Join
You say "this solution is for a single row"? Excellent. Use CROSS APPLY and change the WHERE clause from hand-input literal to the fields of the original table. APPLY operates at row level.
SELECT *
FROM TestModules t
CROSS APPLY
(
SELECT TOP 1 * FROM TestModule_Results
WHERE TestModule_Results.DeviceID = TestModules.DeviceID -- put the connecting fields here
ORDER BY ChangedAt DESC
)tr
I have the following tables.
Customer Customer_id, Customer_name
Customer_details Customer_id,Phone_no,Address
Order_details Order_id,Customer_id,Order_type
I have created a view as following
Create view Orders_Analysis
Select c.Customer_id,cd.phone_no,od.order_id
From customer c
inner join order_details od
on c. Customer_id=od. Customer_id
Inner join Where c. Customer_id=cd. Customer_id
cd. Customer_id=c.Customer_id
Now using the above view and pre mentioned tables I have to extract only those records in the view which are of a particular order_type.
Can you guys suggest me a method.
First of all, You have some mistake in View, This is the correct View after fix mistakes
Create view Orders_Analysis
Select c.Customer_id,cd.phone_no,od.order_id
From customer c
INNER JOIN order_details od
ON c.Customer_id = od.Customer_id
INNER JOIN Customer_details CD
ON c.Customer_id = cd.Customer_id
Now You want to extract only those records in the view which are of a particular order_type.
Solution One:- Because you dont have column order_type in View then use INNER JOIN
SELECT *
FROM Orders_Analysis OA
INNER JOIN Order_details OD
ON OA.order_id = OD.order_id
WHERE OD.Order_type = your value here
Solution Two:-
Otherwise Add Order-Type column in View
Create view Orders_Analysis
Select c.Customer_id,cd.phone_no,od.order_id,od.order_type
From customer c
INNER JOIN order_details od
ON c.Customer_id = od.Customer_id
INNER JOIN Customer_details CD
ON c.Customer_id = cd.Customer_id
and use
Select * from Orders_Analysis where Order_Type = your value here
You need to add the field, od.order_type, to the Select statement results set in your view definition:
SELECT c.Customer_id,cd.phone_no,od.order_id,od.Order_type
Then, if you run a select against the view, specify a WHERE clause on the order_type field for the value you are looking for.
Update your view and select also order type
Select c.Customer_id,cd.phone_no,od.order_id, od.Order_type
You can now execute select query using the view you created
Select * from Orders_Analysis where Order_Type = "any value"
I have this ugly source data with two columns, let's call them EmpID and SomeCode. Generally EmpID maps to the EmployeeListing table. But sometimes, people are entering the Employee IDs in the SomeCode field.
The person previously running this report in Excel 'solved' this problem by performing multiple vlookups with if statements, as well as running some manual checks to ensure results were accurate. As I'm moving these files to Access I am not sure how best to handle this scenario.
Ideally, I'm hoping to tell my queries to do a Left Join on SomeCode if EmpID is null, otherwise Left Join on EmpID
Unfortunately, there's no way for me to force validation or anything of the sort in the source data.
Here's the full SQL query I'm working on:
SELECT DDATransMaster.Fulfillment,
DDATransMaster.ConfirmationNumber,
DDATransMaster.PromotionCode,
DDATransMaster.DirectSellerNumber,
NZ([DDATransMaster]![DirectSellerNumber],[DDATransMaster]![PromotionCode]) AS EmpJoin,
EmployeeLookup.ID AS EmpLookup,
FROM FROM DDATransMaster
LEFT JOIN EmployeeLookup ON NZ([DDATransMaster]![DirectSellerNumber],[DDATransMaster]![PromotionCode]) = EmployeeLookup.[Employee #])
You can create a query like this:
SELECT
IIf(EmpID Is Null, SomeCode, EmpID) AS join_field,
field2,
etc
FROM YourTable
Or if the query will always be used within an Access session, Nz is more concise.
SELECT
Nz(EmpID, SomeCode) AS join_field,
field2,
etc
FROM YourTable
When you join that query to your other table, the Access query designer can represent the join between join_field and some matching field in the other table. If you were to attempt the IIf or Nz as part of the join's ON clause, the query designer can't display the join correctly in Design View --- it could still work, but may not be as convenient if you're new to Access SQL.
See whether this SQL gives you what you want.
SELECT
dda.Fulfillment,
dda.ConfirmationNumber,
dda.PromotionCode,
dda.DirectSellerNumber,
NZ(dda.DirectSellerNumber,dda.PromotionCode) AS EmpJoin,
el.ID AS EmpLookup
FROM
DDATransMaster AS dda
LEFT JOIN EmployeeLookup AS el
ON NZ(dda.DirectSellerNumber,dda.PromotionCode) = el.[Employee #])
But I would use the Nz part in a subquery.
SELECT
sub.Fulfillment,
sub.ConfirmationNumber,
sub.PromotionCode,
sub.DirectSellerNumber,
sub.EmpJoin,
el.ID AS EmpLookup
FROM
(
SELECT
Fulfillment,
ConfirmationNumber,
PromotionCode,
DirectSellerNumber,
NZ(DirectSellerNumber,PromotionCode) AS EmpJoin
FROM DDATransMaster
) AS sub
LEFT JOIN EmployeeLookup AS el
ON sub.EmpJoin = el.[Employee #])
What about:
LEFT JOIN EmployeeListing ON NZ(EmpID, SomeCode)
as your join, nz() uses the second parameter if the first is null, I'm not 100% sure this sort of join works in access. Worth 20 seconds to try though.
Hope it works.
You Could use a Union:
SELECT DDATransMaster.Fulfillment,
DDATransMaster.ConfirmationNumber,
DDATransMaster.PromotionCode,
DDATransMaster.DirectSellerNumber,
EmployeeLookup.ID AS EmpLookup
FROM DDATransMaster
LEFT JOIN EmployeeLookup ON
DDATransMaster.DirectSellerNumber = EmployeeLookup.[Employee #]
where DDATransMaster.DirectSellerNumber IS NOT NULL
Union
SELECT DDATransMaster.Fulfillment,
DDATransMaster.ConfirmationNumber,
DDATransMaster.PromotionCode,
DDATransMaster.DirectSellerNumber,
EmployeeLookup.ID AS EmpLookup
FROM DDATransMaster
LEFT JOIN EmployeeLookup ON
DDATransMaster.PromotionCode = EmployeeLookup.[Employee #]
where DDATransMaster.DirectSellerNumber IS NULL;
i want to create a Index View for full text search.
the only problem i,m facing with subquery, because index views does not allow subquery.
below is my query
ALTER VIEW [dbo].[Demo] with SCHEMABINDING AS
select distinct a.ID,a.Title, a.Description ,b.Name as Recipe, c.Name as Taste , d.Name as CuisineType,
STUFF((SELECT ',' + Name FROM dbo.Ingredients where ID in (select IngredientID from dbo.listingIngredients
where listingid = a.ID ) FOR XML PATH('')), 1, 1, '') as Ingredients
from dbo.Listing as a
inner join dbo.RecipeType b on a.RecipeTypeID = b.ID
inner join dbo.taste c on a.tasteID = c.ID
inner join dbo.CuisineType d on a.CuisineTypeID = d.ID
inner join dbo.listingIngredients e on a.ID = e.listingID
GO
I,m using subquery to get ingredients as concatenate string from Ingredients table using STUFF.
can some one please let me know how can i remove this subquery and have ingredients as contented string.
please let me know
regards
manish
The XML part of the query will cause problems, even if you did manage to remove the sub-selected.
However, all is not lost. You could rewrite the view into a part that can be indexed and another part that is cheaper, but can't. For example, you could write:
ALTER VIEW [dbo].[Demo_Part] with SCHEMABINDING AS
select a.ID,a.Title
, a.Description
, b.Name as Recipe
, c.Name as Taste
, d.Name as CuisineType
, e.name
from dbo.Listing as a
inner join dbo.RecipeType b on a.RecipeTypeID = b.ID
inner join dbo.taste c on a.tasteID = c.ID
inner join dbo.CuisineType d on a.CuisineTypeID = d.ID
inner join dbo.listingIngredients e on a.ID = e.listingID
GROUP BY a.ID,a.Title
, a.Description
, b.Name as Recipe
, c.Name as Taste
, d.Name as CuisineType
, e.name
Depending on your data model, you may not even need the group by. This view can be indexed
And then write another view that is not indexed, but which replaces your original view
CREATE VIEW [dbo].[Demo]
SELECT ...
STUFF (...)
FROM [dbo].[Demo_Part]
As a meta-answer I would add that if you need to index a view like this (and use the DISTINCT), chances are that your data modeller made a pretty big mistake with the data model or that your data access code is very inefficient. Everything about this smells like you are trying to work around poor coding and modelling practices.