Retrieve only the users I don't have in another table - sql

I want to retrieve the users (from users table) that aren't in any group yet (group_user table) and are etudiant type (users.type="etudiant")
I tried this in my function but it give me repeted users from both tables
$Othersetudiants=DB::Table('users')
->join('group_user', 'users.id', '!=', 'group_user.user_id')
->select('users.*')
->where([['users.type','=','etudiant'],
['users.id', '!=', 'group_user.user_id']
])
->get();
Here is my data and tables all foreign keys are set in migrations..
Group:
group_user:
user:

The query built is:
select users.*
from users
[inner] join group_user on
users.id != group_user.user_id
where
users.type = 'etudiant'
and users.id != group_user.user_id
The problem here is in the join clause, you are matching one row of the user table with all rows in the group_user table, except the row that satifies the clause users.id = group_user.user_id.
If you only want the users that are not in the group_user table, you might use LEFT JOIN and then "filter" the users that didn't match the left join:
select users.*
from users
left join group_user on
users.id = group_user.user_id
where
users.type = 'etudiant'
and group_user.user_id is null
For more detailed explanation, see What's the difference between INNER JOIN, LEFT JOIN, RIGHT JOIN and FULL JOIN?

Related

Return records of groupby with a count of one

I have two tables user_expenses and users.
The foreign key for user_expenses is user_expenses.user_id which corresponds to users.id.
I would like to get some information from both tables, in which the users have only ONE expense.
I gave this a shot:
SELECT
users.id, users.email, users.stripe_plan, users.previous_plan,
users.created_at, user_expenses.created_at, user_expenses.description
FROM
users
INNER JOIN
user_expenses ON user_expenses.user_id = users.id
WHERE
user_expenses.description NOT LIKE "%free%"
GROUP BY
user_expenses.user_id
HAVING
COUNT(*) = 1
But of course, this yields the following problem:
SELECT list is not in GROUP BY clause and contains nonaggregated column 'app.user_expenses.created_at' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
Adding this column into the group_by is problematic because it will actually return users who have multiple expenses with different descriptions.
Can anyone offer some advice on how to approach this problem? I only want users with a single entry in the user_expenses table, regardless of the type of description.
You could do either a subquery or you could pseudo-aggregate values that are not in the group by list:
(1):
SELECT users.id, users.email, users.stripe_plan, users.previous_plan, users.created_at, user_expenses.created_at, user_expenses.description
FROM users
INNER JOIN user_expenses
ON user_expenses.user_id = users.id
WHERE user_expenses.description NOT LIKE "%free%"
and users.id not in
(select ue2.user_id from user_expenses ue2 group by user_id having count(*) > 1)
(2)
SELECT users.id, max(users.email), max(users.stripe_plan), max(users.previous_plan), max(users.created_at), max(user_expenses.created_at), max(user_expenses.description)
FROM users
INNER JOIN user_expenses
ON user_expenses.user_id = users.id
WHERE user_expenses.description NOT LIKE "%free%"
GROUP BY user_expenses.user_id
HAVING COUNT(*) = 1
You can do a dummy aggregation. Change:
user_expenses.created_at, user_expenses.description
in the select list by:
min(user_expenses.created_at) created_at, min(user_expenses.description) description
... which will be the same as the original value, since you know you only have one per group.
It would also be more natural to group by the users.id field, which has as advantage that it allows for outer joining the user_expenses table (if ever you would need that):
group by users.id
NB: in MySql 5.7+ it is not necessary to aggregate fields that are functionally dependent on the grouped-by fields. Since all fields of the users record are determined by the users.id value they can go without aggregation.

sql - How to have multiple select/from statements in one query

I'm trying to pull a report where each column is selecting from a specific table set. However, one of the columns needs to pull from a completely different table set and still be included in the same report. Of course, this doesn't work:
select u.first_name, ticket_work.time_spent
FROM tickets LEFT OUTER JOIN ticket_work ON ticket_work.ticket_id = tickets.id JOIN users u
(select count(tickets.id) FROM tickets JOIN users u)
where tickets.assigned_to = u.id
...
So just the part (select count(tickets.id) FROM tickets JOIN users u) needs to be selecting from the different table set but still be included in the report.
I'm a little confused by your question. Are you wanting to return the user, the count of tickets for that user, and the amount of time spent overall? If so, something like this should work:
select u.id, u.first_name,
SUM(tw.time_spent) summed_time_spent,
COUNT(DISTINCT t.id) count_tickets
FROM users u
LEFT JOIN tickets t
ON u.id = t.assigned_to
LEFT JOIN ticket_work tw
ON tw.ticket_id = t.id
GROUP BY u.id, u.first_name
Your questions is unclear, but just generally, it sounds like you're trying to join to a derived table (i.e., a query). In that case, do this:
SELECT...
FROM...
table_A A LEFT JOIN
(SELECT keyfield, valuefield FROM table_b WHERE ...) B
ON A.keyfield = B.keyfield
Does that make sense? To make a derived table, you put a query inside of parenthesis, give it an alias ('B' in this case), and then join it to your other tables as though it were a regular table.
Don't know about your table structure but you may use a sub query for such requirement
select u.first_name, ticket_work.time_spent,(select count(tickets.id) FROM tickets where ticket.id=ticket_work.ticket_id) as myCount
FROM tickets LEFT OUTER JOIN ticket_work ON ticket_work.ticket_id = tickets.id JOIN users u
where tickets.assigned_to = u.id

How to make this join with a TSQL query?

I have a table called USERS that has a foreign key to the table GROUPS (a user can pertain to one or none GROUPS). The table USERS also contains a column ISDELETED (a char column with T or F).
I need a query to retrieve all the GROUPS and all the USERS that are not deleted, if all the users in a GROUP are deleted or no users are defined I need the query to return NULL for that GROUP.
I tried with the following query:
SELECT GROUPS.*, USERS.*
FROM GROUPS INNER JOIN
USERS ON GROUPS.ID = USERS.GROUPID
WHERE USERS.ISDELETED = 'F'
But this query does not returns the groups that are empty. SQL and me are not the best friends in world, some help will be great, thanks.
If you want all the groups, regardless of a match in the users table, you should use a left outer join:
SELECT GROUPS.*, USERS.*
FROM GROUPS
LEFT OUTER JOIN
USERS
ON GROUPS.ID = USERS.GROUPID AND USERS.ISDELETED = 'F'
You should just need to do a left outer join -
SELECT GROUPS.*, USERS.*
FROM GROUPS LEFT OUTER JOIN
USERS ON GROUPS.ID = USERS.GROUPID
WHERE USERS.ISDELETED = 'F'
Here's a reference I like to use to remind myself of the differences in sql joins.
You need to use the LEFT OUTER JOIN operator instead of the INNER JOIN.

Effective resultset structure in case of Inner Join on more than two tables

I was going through this link to see how the effective resultset looks like if we join (Inner/Outer) two tables.
I was just curious to know how the resultset looks like, If we perform Inner/Outer join on more than two tables?
Let's say I've three tables:
1. USERS
2. ADDRESS (Store User's address contains Foreign Key USERS.USER_ID)
3. ITEMS (Contains Items that user sold contains foreign key USERS.USER_ID)
Now If I write a SQL:
SELECT ... FROM USERS users INNER JOIN ADDRESS address ON users.USER_ID = address.USER_ID
INNER JOIN ITEMS items ON users.USER_ID = items.USER_ID WHERE....
How the effective resultset will look like? Whether the DB creates two separate resultset pertaining to each JOIN condition or there will be a single resultset combining all the JOINS? When I say ResultSet, i mean how the effective DB structure looks like (as in the link I've mentioned on top).
Lets me Tell you brief description of the Inner Join And Outer Join.
Simply join is for to view columns from two or more tables together.
Inner Join :- The Matched rows Of the Two or more Tables
SELECT ... FROM USERS users INNER JOIN ADDRESS address ON users.USER_ID = address.USER_ID
INNER JOIN ITEMS items ON users.USER_ID = items.USER_ID WHERE....
What this Statement says is "SELECT ... FROM USERS users INNER JOIN ADDRESS address ON users.USER_ID = address.USER_ID" gives some rows with the filter specified.
INNER JOIN ITEMS items ON users.USER_ID = items.USER_ID WHERE.... :- statement adds columns of the table [ITEMS] with the specified criteria "users.USER_ID = items.USER_ID"
Outer Join :- Different Row of the table mean no rows matched in table.
Two type of the Outer Join
Right Outer :- Not Matched row of the right side table
Left Outer :-Not Matched row of the right side table

SQL help: COUNT aggregate, list of entries and its comment count

So, what I intended to do is to fetch a list of entries/posts with their category and user details, AND each of its total published comments. (entries, categories, users, and comments are separate tables)
This query below fetches the records fine, but it seems to skip those entries with no comments. As far as I can see, the JOINs are good (LEFT JOIN on the comments table), and the query is correct. What did I miss ?
SELECT entries.entry_id, entries.title, entries.content,
entries.preview_image, entries.preview_thumbnail, entries.slug,
entries.view_count, entries.posted_on, entry_categories.title AS category_title,
entry_categories.slug AS category_slug, entry_categories.parent AS category_parent,
entry_categories.can_comment AS can_comment, entry_categories.can_rate AS can_rate,
users.user_id, users.group_id, users.username, users.first_name, users.last_name,
users.avatar_small, users.avatar_big, users.score AS user_score,
COUNT(entry_comments.comment_id) AS comment_count
FROM (entries)
JOIN entry_categories ON entries.category = entry_categories.category_id
JOIN users ON entries.user_id = users.user_id
LEFT JOIN entry_comments ON entries.entry_id = entry_comments.entry_id
WHERE `entries`.`publish` = 'Y'
AND `entry_comments`.`publish` = 'Y'
AND `entry_comments`.`deleted_at` IS NULL
AND `category` = 5
GROUP BY entries.entry_id, entries.title, entries.content,
entries.preview_image, entries.preview_thumbnail, entries.slug,
entries.view_count, entries.posted_on, category_title, category_slug,
category_parent, can_comment, can_rate, users.user_id, users.group_id,
users.username, users.first_name, users.last_name, users.avatar_big,
users.avatar_small, user_score
ORDER BY posted_on desc
edit: I am using MySQL 5.0
Well, you're doing a left join on entry_comments, with conditions:
`entry_comments`.`publish` = 'Y'
`entry_comments`.`deleted_at` IS NULL
For the entries with no comments, these conditions are false.
I guess this should solve the problem:
WHERE `entries`.`publish` = 'Y'
AND (
(`entry_comments`.`publish` = 'Y'
AND `entry_comments`.`deleted_at` IS NULL)
OR
`entry_comments`.`id` IS NULL
)
AND `category` = 5
In the OR condition, I put entry_comments.id, assuming this is the primary key of the entry_comments table, so you should replace it with the real primary key of entry_comments.
It's because you are setting a filter on columns in the entry_comments table. Replace the first with:
AND IFNULL(`entry_comments`.`publish`, 'Y') = 'Y'
Because your other filter on this table is an IS NULL one, this is all you need to do to allow the unmatched rows from the LEFT JOIN through.
Try changing the LEFT JOIN to a LEFT OUTER JOIN
OR
I'm no expert with this style of SQL joins (more of an Oracle man myself), but the wording of the left join is leading me to believe that it is joining entry_comments on to entries with entry_comments on the left, you really want it to be the other way around (I think).
So try something like:
LEFT OUTER JOIN entries ON entries.entry_id = entry_comments.entry_id