Question

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?

Was it helpful?

Solution

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.

OTHER TIPS

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top