There is a lot of questions regarding this error message type, but I have not found -or maybe not understood- any answers on this specific case.
I have an application that can connect to custom database and retrieve data within a certain time span. Sort order can also be set.
Different views can be used to retrieve data and the application shall not have any knowledge of which columns that exists in the view.
But the application must know which column in the underlying table that shall be used to find the time span and which column in the underlying table that shall be used to set sort order.
How can I do that when I do not want this information to be returned as a part of the “select” statement?
/*--------- This is an example of view in the custom database ------------*/
CREATE VIEW [dbo].[EventViewFull]
AS
SELECT
dbo.[Events].[Time],
dbo.[Events].MicroSec,
dbo.[Events].Seq,
dbo.Redundancy.Redundancy,
dbo.OrigStation.OrigStation,
dbo.Priority.Priority,
dbo.EventType.EventType,
dbo.CommGrp.CommGrp,
dbo.AlarmState.AlarmState,
dbo.[Events].Acked,
dbo.Names1.Name1,
dbo.Names2.Name2,
dbo.[Events].EventText,
dbo.[Events].[Description],
dbo.[Events].AddInfo,
dbo.[Events].Members
FROM
dbo.[Events]
INNER JOIN dbo.Names1 ON dbo.[Events].Name1_ID = dbo.Names1.Name1_ID
INNER JOIN dbo.Names2 ON dbo.[Events].Name2_ID = dbo.Names2.Name2_ID
INNER JOIN dbo.AlarmState ON dbo.[Events].AlarmState_NR = dbo.AlarmState.AlarmState_NR
INNER JOIN dbo.EventType ON dbo.[Events].EventType_NR = dbo.EventType.EventType_NR
INNER JOIN dbo.CommGrp ON dbo.[Events].CommGrp_NR = dbo.CommGrp.CommGrp_NR
INNER JOIN dbo.Priority ON dbo.[Events].Priority_NR = dbo.Priority.Priority_NR
INNER JOIN dbo.OrigStation ON dbo.[Events].OrigStation_NR = dbo.OrigStation.OrigStation_NR
LEFT JOIN dbo.Redundancy ON dbo.[Events].OrigStation_NR = dbo.Redundancy.Redun
/*--This is the not working query in the application. SQL server, database name, time span and view is input from the user -----*/
select * from EventViewFull
where [Event].[dbo].[Events].UTCTime between '2012-11-18 23:0:0' and '2014-6-18 22:0:0'
order by [Event].[dbo].[Events].UTCTime DESC, [Event].[dbo].[Events].Seq DESC
/* Error messages I get with the current implementation of the script.
The multi-part identifier "Event.dbo.Events.UTCTime" could not be bound.
The multi-part identifier "Event.dbo.Events.Seq" could not be bound.
*/
I hope I understood your problem properly
You can use the following query to return all the columns only from the view
select EventViewFull.* from EventViewFull
join [Event].[dbo].[Events] ev ON EventViewFull.Seq = ev.Seq
where ev.UTCTime between '2012-11-18 23:0:0' and '2014-6-18 22:0:0'
order by ev.UTCTime DESC, ev.Seq DESC
This query will eliminate the error what you are getting at the moment. But I assumed Seq column as the primary key for the event. You need to change this to the correct column, if it is not the right one.
However as you can see the query, [Event].[dbo].[Events] is joined again with the view, even though the view has the information from the same table. It would be good to add UTCTime to the view, from the performance point of view, but not sure why does that not work for you.
I had to add UTCTime and Seq to the select statement in the [dbo].[EventViewFull] event though I did not want UTCtime and Seq to be a part of the resulting data set, but I can live with it.
Related
I am a Tableau designer, and we are building some views that get filtered by category a lot. Because of this, we tried to create a category_id that would serve as partition. The problem seems to be that if I filter data category only, the partition doesn't get used and the total table GB and cost gets hit.
Our team is trying to see if this could be minimized by using a nested query as follows:
SELECT *
FROM table a
INNER JOIN (
SELECT DISTINCT category_id, category
FROM table
) b
ON a.category_id = b.category_id
WHERE b.category = 'Category A'
The idea is that we could show the user b.category, they select it in Tableau and then the inner join would kick off the partition and limit the bytes returned. When I try this in the BQ interface, the estimated returned size comes back the same.
You'll need to filter on the partitioned field before you make the inner join.
I haven't used tableau before so don't know if this is possible but just an idea. You could create a parameter which is set by the chosen category in tableau, which could be referenced in the where statement of the partitioned table?
SELECT *
FROM table a
INNER JOIN (
SELECT DISTINCT category_id, category
FROM table
Where category = #chosen_category
) b
ON a.category_id = b.category_id;
When you say that your attempts to filter only by category, the partition isn't used, have you actually tested querying the table from the console to test whether the partition is being used or not. If it isn't then you need to look at the partition, but if it is, then you would need to take another look at your Tableau query.
VizQL (Viz query language) is Tableau's sql parser that converts your Tableau viz into SQL for execution, so whilst you cannot really modify the outgoing SQL, you can at least capture it and test which enables you to identify poor performing calculations and/or vizzes, as well as optimise the backend for the queries that Tableau will send.
I've written an article about this here: https://datawonders.atlassian.net/wiki/spaces/TABLEAU/pages/1290600449/Let+s+Talk+Errors+Tuning+6+minute+read
The thing about Tableau is that it treats the source as a derived table, with all filters being placed at the upper-level of the query immediately before the stream,
so your query:
Select *
From table a
Join (
Select Distinct Category_ID, Category
From table
)b On a.category_id = b.category_id
Where b.category = 'Category A'
Will actually look like this (assuming you just select everything):
Select a1.*
From (
Select *
From table a
Join (
Select Distinct Category_ID, Category
From table
)b On a.category_id = b.category_id
)a1
Where a1.category = <your selected category>
So you can see from here that being two-levels deep, your Category table just won't be hit, instead everything shall be read into the spool, the join taking place in tempdb, and only the complete set is filtered immediately before streaming to Tableau.
Bad, underperforming sql it most certainly is.
And this is where the relational method of v2020.2 comes into play, as this has been designed to treat each table as a separate exclusive entity, joins are only made at execution time, so you could build a view that uses data from table a where you are using table b to provide the filtering.
As an alternative, and my preferred overall method is to switch entirely to Custom SQL, utilising this with parameters, as this will enable you to craft and test your own sql to create your own high-performance, low-loading query, but as parameters are parsed before the query is executed, you can place the filtering deep down in the query without the need for a secondary look-up table or filtered derived statement - a select distinct as you are currently using it is still going to produce a large plan, as unless the category column is indexed, the engine shall still need to read every record from the table.
So using parameters, your new query will look something like:
Select a1.*
From (
Select *
From table a
Join lookup_table b On On a.category_id = b.category_id
And b.category = <parameters.pCategory>
)a1
(I've placed the filter condition directly onto the join as this can improve performance in some circumstances, though this actually shouldn't make much difference)
And when used in conjunction with the Set parameter action, you can now use parameters as in/out updateable variables which shall update as the user interacts directly with the viz, instead of the user needing to manually update as they go. If you haven't used these before, I wrote an article about it here: https://community.tableau.com/s/news/a0A4T00000313S0UAI/psst-have-you-had-a-go-with-variables-in-tableau-yet
Steve
I'm trying to put together a query that updates a field within a table. I'm attempting to run a sub select query that gives me a number, and then use that number that resulted from the sub-query as part of the criteria for the update query.
USE EMMS
Update [2_import_VZW_tbl_SMTN]
set [2_import_VZW_tbl_SMTN].[Client_ID] =[tbl_Foundation_Account].[Client_ID]
where ([tbl_Foundation_Account].[Foundation_Account_ID] =
(Select TOP 1 tbl_Foundation_Account.Foundation_Account_ID
FROM tbl_Foundation_Account
INNER JOIN [2_Import_tbl_AWCDSU]
ON tbl_Foundation_Account.Foundation_Account_ID =
[2_Import_tbl_AWCDSU].[ECPD Profile ID]))
My issue is I keep receiving this error
The multi-part identifier
tbl_Foundation_Account.Foundation_Account_ID" could not be bound.
Am I using the sub-query incorrectly? When I've received this error before, it's been because of some ambiguity in the table or field names, but this time I've checked for all that and it should be fine. Can anyone explain what SQL sin I have committed?
On the error
The multi-part identifier
tbl_Foundation_Account.Foundation_Account_ID" could not be bound.
This is because the table column [tbl_Foundation_Account].[Client_ID] does not exists in the scope of outer UPDATEquery .
The only table the outer query has an inkling about is [2_import_VZW_tbl_SMTN] and it does not have a column like [tbl_Foundation_Account].[Client_ID].
It is akin to writing a column name with a typo or like you said
When I've received this error before, it's been because of some
ambiguity in the table or field names
Please try a query like below.
Note that I am using Inner query syntax and ensuring that a single value is returned by using
select top 1 [Client_ID]
in the inner query. rest of the query syntax is same.
USE EMMS
Update [2_import_VZW_tbl_SMTN]
set [2_import_VZW_tbl_SMTN].[Client_ID] =
(
select top 1 [Client_ID]
from [tbl_Foundation_Account]
where [Foundation_Account_ID] =
(
Select TOP 1 a.Foundation_Account_ID
FROM tbl_Foundation_Account a
INNER JOIN [2_Import_tbl_AWCDSU] b
ON a.Foundation_Account_ID = b.[ECPD Profile ID]
)
)
Another poster submitted this answer earlier, but then deleted it. I was able to try it before they deleted it and it works exactly how I needed it to work. I will use this as the right answer unless someone else can tell me why this is a bad Idea.
USE EMMS
Update [2_import_VZW_tbl_SMTN]
set [2_import_VZW_tbl_SMTN].[Client_ID] = [tbl_Foundation_Account].[Client_ID]
from [tbl_Foundation_Account]
where ([tbl_Foundation_Account].[Foundation_Account_ID] =
(Select TOP 1 tbl_Foundation_Account.Foundation_Account_ID
FROM tbl_Foundation_Account
INNER JOIN [2_Import_tbl_AWCDSU]
ON tbl_Foundation_Account.Foundation_Account_ID = [2_Import_tbl_AWCDSU].[ECPD Profile ID]))
I have an order system. Users with can be attached to different orders as a type of different user. They can download documents associated with an order. Documents are only given to certain types of users on the order. I'm having trouble writing the query to check a user's permission to view a document and select the info about the document.
I have the following tables and (applicable) fields:
Docs: DocNo, FileNo
DocAccess: DocNo, UserTypeWithAccess
FileUsers: FileNo, UserType, UserNo
I have the following query:
SELECT Docs.*
FROM Docs
WHERE DocNo = 1000
AND EXISTS (
SELECT * FROM DocAccess
LEFT JOIN FileUsers
ON FileUsers.UserType = DocAccess.UserTypeWithAccess
AND FileUsers.FileNo = Docs.FileNo /* Errors here */
WHERE DocAccess.UserNo = 2000 )
The trouble is that in the Exists Select, it does not recognize Docs (at Docs.FileNo) as a valid table. If I move the second on argument to the where clause it works, but I would rather limit the initial join rather than filter them out after the fact.
I can get around this a couple ways, but this seems like it would be best. Anything I'm missing here? Or is it simply not allowed?
I think this is a limitation of your database engine. In most databases, docs would be in scope for the entire subquery -- including both the where and in clauses.
However, you do not need to worry about where you put the particular clause. SQL is a descriptive language, not a procedural language. The purpose of SQL is to describe the output. The SQL engine, parser, and compiler should be choosing the most optimal execution path. Not always true. But, move the condition to the where clause and don't worry about it.
I am not clear why do you need to join with FileUsers at all in your subquery?
What is the purpose and idea of the query (in plain English)?
In any case, if you do need to join with FileUsers then I suggest to use the inner join and move second filter to the WHERE condition. I don't think you can use it in JOIN condition in subquery - at least I've never seen it used this way before. I believe you can only correlate through WHERE clause.
You have to use aliases to get this working:
SELECT
doc.*
FROM
Docs doc
WHERE
doc.DocNo = 1000
AND EXISTS (
SELECT
*
FROM
DocAccess acc
LEFT OUTER JOIN
FileUsers usr
ON
usr.UserType = acc.UserTypeWithAccess
AND usr.FileNo = doc.FileNo
WHERE
acc.UserNo = 2000
)
This also makes it more clear which table each field belongs to (think about using the same table twice or more in the same query with different aliases).
If you would only like to limit the output to one row you can use TOP 1:
SELECT TOP 1
doc.*
FROM
Docs doc
INNER JOIN
FileUsers usr
ON
usr.FileNo = doc.FileNo
INNER JOIN
DocAccess acc
ON
acc.UserTypeWithAccess = usr.UserType
WHERE
doc.DocNo = 1000
AND acc.UserNo = 2000
Of course the second query works a bit different than the first one (both JOINS are INNER). Depeding on your data model you might even leave the TOP 1 out of that query.
Due to a variety of design decisions, we have a table, 'CustomerVariable'. CustomerVariable has three bits of information--its own id, an id to Variable (a list of possible settings the customer can have), and the value for that variable. The Variable table, on the other hand, has the information on a default--in case the CustomerVariable is not set.
This works well in most situations, allowing us not to have to create an insanely long list of information--especially in a case where there are 16 similar variables that need to be handled for a customer.
The problem comes in trying to get this information into a select. So far, our 'best' solution involves far too many joins to be efficient--we get a list of the 16 VariableIds we need information on, setting them into variables, first. Later on, however, we have to do this:
CROSS JOIN dbo.Variable v01
LEFT JOIN dbo.CustomerVariable cv01 ON cv01.customerId = c.id
AND cv01.variableId = v01.id
CROSS JOIN dbo.Variable v02
LEFT JOIN dbo.CustomerVariable cv02 ON cv02.customerId = c.id
AND cv02.variableId = v02.id
-- snip --
CROSS JOIN dbo.Variable v16
LEFT JOIN dbo.CustomerVariable cv16 ON cv16.customerId = c.id
AND cv16.variableId = v16.id
WHERE
v01.id = #cv01VariableId
v02.id = #cv02VariableId
-- snip --
v16.id = #cv16VariableId
I know there has to be a better way, but we can't seem to find it amidst crunch time. Any help would be greatly appreciated.
If your data set is relatively small and not too volatile, you may want to use materialized views (assuming your database supports them) to optimize the lookup.
If materialized views are not an option, consider writing a stored procedure that retrieves that data in two passes:
First retrieve all of the CustomerVariables available for a particular customer (or set of customers)
Next, retrieve all of the default values from the Variables table
Perform a non-distinct union on the results merging the defaults in wherever a CustomerVariable record is missing.
Essentially, this is the equivalent of:
SELECT variableId,
CASE WHEN CV.variableId = NULL THEN VR.defaultValue ELSE CV.value END
FROM Variable VR
LEFT JOIN CUstomerVariable CV on CV.variableId = VR.variableId
WHERE CV.customerId = c.id
The type of query you want is called a pivot table or crosstab query.
By far the easiest way of dealing with this is to create a view based off of a crosstab query. This will flip the columns from being vertical to being horizontal like a regular sql table. Once that is done, just query the view. Easy. ;)
One doubt in MSSQL.
There are two tables in a databases.
Table 1 named Property contain
fields PRPT_Id(int),PRPT_Name(varchar), PRPT_Status(bit)
Table 2 named PropertyImages contain fields PIMG_Id(int),PIMG_ImageName(varchar),PRPT_Id(int),PIMG_Status(bit)
These two tables follow a one-to-many relationship.
That means the each Property can have zero, one or more PropertyImages corresponding to it.
What is required is a query to display
PRPT_Id, PRPT_Name, ImageCount(Count of all images corresponding to a PRPT_Id where PIMG_Status is true. o if there arent any images), FirstImageName(if there are n images, the name of the first image in the image table corresponding to the PRPT_Id with PIMG_Status true. if there aren't any images we fill that with whitespace/blank) . another condition is that PRPT_Status should be true.
Edit Note - Both the tables are having autoincremented integers as primary key.
So first Image name will be the name with MIN(PIMG_Id), isn't that so?
I want the PIMG_ImageName corresponding to the MIN(PIMG_ID) in the resultset
Assuming that FirstImage means the one with the lowest Id, then this should be at least close enough to test to completion:
SELECT
PRPT_Id,
PRPT_Name,
ISNULL(pi1.ImageName, '') AS FirstImageName,
COUNT(1) AS ImageCount
FROM Property AS p
LEFT JOIN PropertyImages AS pi
ON p.PRPT_Id = pi.PRPT_Id
LEFT JOIN PropertyImage AS pi1
ON p.PRPT_Id = pi1.PRPT_Id
LEFT JOIN PropertyImgage AS pi2
ON p.PRPT_Id = pi2.PRPT_Id
AND pi1.PIMG_Id > pi2.PIMG_Id
WHERE PRPT_Status = TRUE
AND pi1.PIMG_Status = TRUE
AND pi2.PIMG_ImageName IS NULL
The double LEFT JOIN assures that you get the first image record in pi1. If the "First" rule is different, then adjust this join accordingly.
This should be about as efficient as possible. It has no subqueries.
It seems that you have to write nested queries to display what you want.
If that's the case (I'm no SQL expert), I'd recommend you to start with the innermost query, then you go out until you reach the outermost (and final) query.
First, you have to retrieve the PIMGs and group them by PRPT.
SELECT PRPT_Id, COUNT(PIMG_Id) AS PRPT_ImageCount, MIN(PIMG_Id) AS PRPT_MinImage
FROM PropertyImages
GROUP BY PRPT_Id
That retrieves the PRPT_Id's of the properties that do have associated images. However, you don't get any results for the properties that don't have any associated images.
After that, we will left join the Properties table with the previous query. The left join ensures that all the Properties will be retrieved, even if they don't appear in the results of the right query (that is, even if they don't have any associated images).
SELECT Properties.*, PRPT_ImageCount, PRPT_MinImage
FROM Properties LEFT JOIN (
SELECT PRPT_Id, COUNT(PIMG_Id) AS PRPT_ImageCount, MIN(PIMG_Id) AS PRPT_MinImage
FROM PropertyImages
GROUP BY PRPT_Id ) Temp ON ( Properties.PRPT_Id = Temp.PRPT_Id )
I hope that my SQL isn't wrong and that this post helps you.
Regards,
Edit:
SELECT Properties.*,
PRPT_ImageCount,
PRPT_MinImage,
PIMG_ImageName
FROM ( Properties LEFT JOIN
( SELECT PRPT_Id,
COUNT(PIMG_Id) AS PRPT_ImageCount,
MIN(PIMG_Id) AS PRPT_MinImage
FROM PropertyImages
GROUP BY PRPT_Id ) Temp1
ON ( Properties.PRPT_Id = Temp1.PRPT_Id ) ) Temp2 LEFT JOIN
PropertyImages ON ( PropertyImages.PIMG_Id = Temp2.PRPT_MinImage )
Now, I'm really unsure of my SQL.
the name of the first image in the image table corresponding to the PRPT_Id with PIMG_Status true
You may want to define "first" in this context. Tables aren't really ordered so, unless you keep your own ordering, the term first needs to mean "first one found".
Assuming the above is true (you want first found image), then that's the only real hard part of the query (and the type of thing that's tripped me up before). The rest of it seems fairly straight forward. If I can find the time tomorrow, I'll try to put something together for you... but it seems likely someone else will be able to provide the answer before then.