How to make this join with a TSQL query? - sql

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.

Related

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

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?

SQL QUERY data reading

I have two table country and Users. I want to view the country name which have user disabled. So i wrote query for this.
SELECT DISTINCT cntr_id,cntr_name FROM
(SELECT COUNTRY.cntr_id, COUNTRY.cntr_name, USERS.user_enabled,
USERS.user_name, USERS.user_id
FROM COUNTRY INNER JOIN Users
ON COUNTRY.cntr_id = USERS.cntr_id
)
AS TAB where user_enabled = 0
My questions are :
Is this inner query?
will the query fetch all the countries (include user enabled) from the database before running outer query?
Is there any other method to select?
Yes it's an inner Query and will fetch what you need, but you don't need your outer SELECT query. You could just do it like:
SELECT DISTINCT COUNTRY.cntr_id, COUNTRY.cntr_name
FROM COUNTRY INNER JOIN Users
ON COUNTRY.cntr_id = USERS.cntr_id
WHERE USERS.user_enabled = 0
Yes, that is inner query.
Yes, inner query will fetch all the countries (including user enabled). Outer query will remove those records later.
You don't have to use outer query for this.
SELECT DISTINCT C.cntr_id, C.cntr_name
FROM COUNTRY C INNER JOIN
Users U ON C.cntr_id = U.cntr_id
WHERE U.user_enabled=0
OR
SELECT C.cntr_id, C.cntr_name
FROM COUNTRY C INNER JOIN
Users U ON C.cntr_id = U.cntr_id
WHERE U.user_enabled=0
GROUP BY C.cntr_id, C.cntr_name
To answer your question,
Yes It is inner and outer two queries.
The inner query will fetch the all rows which satisfy the join condition.
Yes there is other method to select where you can directly apply the filter in inner query and remove the outer select.
SELECT COUNTRY.cntr_id, COUNTRY.cntr_name, USERS.user_enabled,
USERS.user_name, USERS.user_id
FROM COUNTRY INNER JOIN Users
ON COUNTRY.cntr_id = USERS.cntr_id
WHERE
USERS.user_enabled = 0

Selecting multiple tables

This is a hypothetical (maybe even a naive) example without using joins. Consider you have a users table and you have to list all users assigned to a particular role. A SQL statement could be expressed as:
SELECT u.username
FROM users u, roles r
WHERE (u.roleid = r.id AND r.id = 5);
If you assume that a relation exists, can the following mean the same thing or a join of some sort is required?:
SELECT u.username
FROM users u, roles r
WHERE r.id = 5;
The second query is an example of an implicit cross join (aka Cartesian join) - every record from users will be joined to every record from roles with id=5, since all these combinations will have the where clause evaluate as true.
A join will be required to have correct data returned
SELECT u.username
FROM users u
JOIN roles r
ON u.roleid = r.id
WHERE r.id = 5;
I think is better to use explicit join with ON to dtermine which columns have relationship rather than using realtionship in WHERE clause
You need two columns of the same type one for each table to JOIN .You need the join-predicate ON u.roleid = r.id to get the correct data .
Inner join creates a new result table by combining column values of two tables (A and B) based upon the join-predicate. The query compares each row of A with each row of B to find all pairs of rows which satisfy the join-predicate. When the join-predicate is satisfied, column values for each matched pair of rows of A and B are combined into a result row. The result of the join can be defined as the outcome of first taking the Cartesian product (or Cross join) of all records in the tables (combining every record in table A with every record in table B)—then return all records which satisfy the join predicate.
No. In the second version you have no relationship between the tables. The , operator in the from clause means cross join. The second example will either return all users at least once (depending on the number of matched in the second table). Or it will return no rows (if there are no matches in the second table).
If the second example were:
SELECT u.username
FROM users u, roles r
WHERE r.id = 5 and u.id = 5
Then they would mean the same thing. The clearer and better way to write this is:
SELECT u.username
FROM users u cross join roles r
WHERE r.id = 5 and u.id = 5
Or using proper inner join syntax:
SELECT u.username
FROM users u join
roles r
on r.id = u.id
WHERE r.id = 5 /* this could also be in the `on` clause */

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

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