Creating a view with a 'WHERE NOT IN' SQL subquery - sql

I'm trying to turn the following SQL query into a view.
SELECT * FROM system_accounts
WHERE system_accounts.id
NOT IN (SELECT account_id
FROM system_group_members
WHERE system_group_members.group_id = 1);
In other words, how do I go about creating a view that gives me all the non-members of a certain group, with those non-members coming from the total set of accounts known to the system?
The query works fine when I test it in my Adminer window. However, I'm at a loss how to express the variable group_id (which in my example is '1') in correct SQL for a view.
I'm sure I'm missing something trivial, but this is the sort of thing I'll bang my head on for hours. Hopefully some kind soul here will help me out.
Many thanks for your time.

You can create a view that has all non-members of all groups. Then you can filter down to the group you want:
CREATE VIEW v_nonmembers AS
SELECT g.group_id, sa.*
FROM system_groups g cross join
system_accounts sa LEFT JOIN
system_group_members sgm
ON gsm.group_id = g.group_id AND
gsm.account_id = sa.id
WHERE gsm.group_id IS NULL;
You can then use it as:
SELECT *
FROM v_nonmembers
WHERE group_id = 1;

Related

SQL - all join statement?

I am learning SQL and have been tasked with creating a query for this:
-write a select all join statement on userDb.user and userDb.advertiser based on network_id where user_name = 'finance'
However, I cant find anything that suggests an all joint statement so I am confused on what to do?
My attempt so far however, does not work!
SELECT userDB.user.network_id,
userDB.advertiser.network_id
FROM userDB.user
INNER JOIN userDB.advertiser ON userDB.user.network_id = userDB.advertiser.network_id
WHERE network_id = user_name = 'finance'
What is the correct code and breakdown explanation?
Ok, the query you're looking for is something like this;
SELECT *
FROM user AS u
INNER JOIN advertiser AS a
ON u.network_id = a.network_id
WHERE u.user_name = 'finance'
You were almost there. The changes I've made is to use table alias' ('u' and 'a') which it's worth reading up on. Your where clause also had too many operators in there.
The SELECT * is what I guess they mean by SELECT ALL
Let me know if you don't understand any of the query above and I'll be happy to help.

In an EXISTS can my JOIN ON use a value from the original select

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.

LOV query is invalid

I am working with APEX 4.2.1.00.08 and I keep getting the validation error "LOV query is invalid, a display and a return value are needed, the column names need to be different. If your query contains an in-line query, the first FROM clause in the SQL statement must not belong to the in-line query.". I'm not sure what's causing this. My sql is:
SELECT u1.name d, susu.subunitid r
FROM basic.subunitsuperunit susu
INNER JOIN basic.unit u1 ON susu.subunitid = u1.unitid
INNER JOIN basic.unit u2 ON susu.superunitid = u2.unitid
WHERE level = 3
AND u1.name != 'XYZ'
CONNECT BY PRIOR subunitid = superunitid
START WITH u1.name = (SELECT u3.name FROM basic.unit u3 WHERE u3.unitid = (SELECT TO_NUMBER(gp.value) FROM basic.global_parameters gp WHERE gp.name = 'A_UNIT'))
ORDER BY u1.name
I have made sure there was no semicolon, and I have checked this query in SQL Developer and it runs fine. I already realize those subqueries aren't optimal, but what am I doing wrong here?
Try to create an apex_collection or a database view with your query and then create your lov based on the new object.
I too face same kind of difficulties with other queries and I always solved it so.
This happens also if you begin your query using the WITH clause. You can wrap the whole lot up with
SELECT name d, ID r
From (your query here)
I see this post is old, but I ran into the same error in the "Lists of Values" portion of the application. My issue happened to be that a grant was missing for the applicaton's run as user.

OR query performance and strategies with Postgresql

In my application I have a table of application events that are used to generate a user-specific feed of application events. Because it is generated using an OR query, I'm concerned about performance of this heavily used query and am wondering if I'm approaching this wrong.
In the application, users can follow both other users and groups. When an action is performed (eg, a new post is created), a feed_item record is created with the actor_id set to the user's id and the subject_id set to the group id in which the action was performed, and actor_type and subject_type are set to the class names of the models. Since users can follow both groups and users, I need to generate a query that checks both the actor_id and subject_id, and it needs to select distinct records to avoid duplicates. Since it's an OR query, I can't use an normal index. And since a record is created every time an action is performed, I expect this table to have a lot of records rather quickly.
Here's the current query (the following table joins users to feeders, aka, users and groups)
SELECT DISTINCT feed_items.* FROM "feed_items"
INNER JOIN "followings"
ON (
(followings.feeder_id = feed_items.subject_id
AND followings.feeder_type = feed_items.subject_type)
OR
(followings.feeder_id = feed_items.actor_id
AND followings.feeder_type = feed_items.actor_type)
)
WHERE (followings.follower_id = 42) ORDER BY feed_items.created_at DESC LIMIT 30 OFFSET 0
So my questions:
Since this is a heavily used query, is there a performance problem here?
Is there any obvious way to simplify or optimize this that I'm missing?
What you have is called an exclusive arc and you're seeing exactly why it's a bad idea. The best approach for this kind of problem is to make the feed item type dynamic:
Feed Items: id, type (A or S for Actor or Subject), subtype (replaces actor_type and subject_type)
and then your query becomes
SELECT DISTINCT fi.*
FROM feed_items fi
JOIN followings f ON f.feeder_id = fi.id AND f.feeder_type = fi.type AND f.feeder_subtype = fi.subtype
or similar.
This may not completely or exactly represent what you need to do but the principle is sound: you need to eliminate the reason for the OR condition by changing your data model in such a way to lend itself to having performant queries being written against it.
Explain analyze and time query to see if there is a problem.
Aso you could try expressing the query as a union
SELECT x.* FROM
(
SELECT feed_items.* FROM feed_items
INNER JOIN followings
ON followings.feeder_id = feed_items.subject_id
AND followings.feeder_type = feed_items.subject_type
WHERE (followings.follower_id = 42)
UNION
SELECT feed_items.* FROM feed_items
INNER JOIN followings
followings.feeder_id = feed_items.actor_id
AND followings.feeder_type = feed_items.actor_type)
WHERE (followings.follower_id = 42)
) AS x
ORDER BY x.created_at DESC
LIMIT 30
But again explain analyze and benchmark.
To find out if there is a performance problem measure it. PostgreSQL can explain it for you.
I don't think that the query needs simplifying, if you identify a performance problem then you may need to revise your indexes.

SQL primer: Why and how to use join statements?

I have a MySQL database that looks like this:
users
( id , name )
groups
( id , name )
group_users
( id , group_id , user_id )
Now to look up all the groups that a user belongs to, I would do something like this:
select * from 'group_users' where 'user_id' = 47;
This will probably return something like:
( 1 , 3 , 47 ),
( 2 , 4 , 47 ),
But when I want to display this to the user, I'm going to want to display the name of the groups that they belong to instead of the group_id. In a loop, I could fetch each group with the group_id that was returned, but that seems like the wrong way to do it. Is this a case where a join statement should be used? Which type of join should I use and how would I use it?
In general, you want to reduce the total number of queries to the database, by making each query do more. There are many reasons why this is a good thing, but the main one is that relational database management systems are specifically designed to be able to join tables quickly and are better at it than the equivalent code in some other language. Another reason is that it's usually more expensive to open many little queries than it is to run one large query that has everything you'll end up needing.
You want to take advantage of your RDBMS's strengths, so you should try to push data access into it in a few big queries rather than lots of little queries.
Now, that's just a general rule of thumb. There are cases when it's better to do some things outside of the database. It's important that you determine which is the right case for your situation by looking into bottlenecks if and only if they occur. Don't spend time worrying about performance until you find a performance problem.
But, in general, it's better to handle joins, lookups and all other query-related tasks in the database itself than it is to try to handle it in a general-purpose language.
That said, the kind of join you want is an inner join. You'd structure your join query like this:
SELECT groups.name, group_users.user_id
FROM group_users
INNER JOIN groups
ON group_users.group_id = groups.group_id
WHERE groups.user_id = 47;
I always find these charts to be very useful when doing joins:
https://blog.codinghorror.com/a-visual-explanation-of-sql-joins/
SELECT gu.id, gu.group_id, g.name, gu.user_id
FROM group_users gu
INNER JOIN Group g
ON gu.group_id = g.group_id
WHERE user_id = 47
That's a typical case for an inner join, such as:
select users.name, group.name from groups
inner join group_users on groups.id = group_users.group_id
inner join users on group_users.user_id = users.id
where user_id = 47