NON Equi Join on more than 2 tables - mysql

I am trying to fetch out records from left table only those records which are not in right table. I have near about 5000 records in the left table. Similarly, the same thing is need to be done on more than 3-4 tables. I have to find out records from first table that are not in rest of my 3-4 tables. The same primary or foreign key concept is working their.
My first attempt taking two tables fetch out 5,70,000 records from original 5k records. Repeating the record.
SELECT m.* FROM members m, pinnumber p where p.pinmemberid != m.memberid
My second attempt also made my-sql browser hang.
SELECT m.* FROM members m
LEFT JOIN pinnumber p ON p.pinmemberid != m.memberid
LEFT JOIN customer c ON m.memberid != c.memberid
My third attempt is also making a lot of time
SELECT * FROM members m
WHERE 1=1 AND AND not exists ( select 1 from pinnumber p where 1=1 And
p.pinmemberid = m.memberid AND p.pinproductid LIKE '%Remit%')
AND not exists ( select 1 from customer c where 1=1 and c.card_name is not null AND m.memberid = c.memberid )
Please suggest me what to do with this. If I need to put non-equi join on this.

This query will try to join the first table members with tables pinnumber and customer:
SELECT m.*
FROM
members m
LEFT JOIN pinnumber p ON p.pinmemberid = m.memberid
LEFT JOIN customer c ON m.memberid = c.memberid
WHERE p.pinmemberid is null and c.memberid is null
since we are using a left join, the query will return all rows of members but if the join doesn't succeed pinmemberid and memberid will be null. The rows in which those values are null are the ones that doesn't exist in the right tables. You might want to use OR instead of AND.
If you use a join with != condition, you will get all rows of the first table multiplied for all rows of the second table, except the ones that have the same id.
Another way to get the same result is this:
SELECT members.*
FROM members
WHERE
memberid not in (select pinmembedid from pinnumber)
and memberid not in (select memberid from customer)
(substitute AND with OR if you wish). NOT IN clause might be a little slower, but it's easier to understand.

By the way, what you were trying to do is:
SELECT m.* FROM members m
LEFT JOIN pinnumber p ON p.pinmemberid = m.memberid
LEFT JOIN customer c ON m.memberid = c.memberid
where p.pinmemberid is null and c.pinmemberid is null
The left join produces NULL values when there are no records in the right table. You can filter for these after the join.

I hope this command will produce your desired output..
SELECT m.* FROM members m LEFT JOIN pinnumber p ON p.pinmemberid = m.memberid where m.memberid is null;

Related

How to get orders count sub-queries

I have some difficuties to get orders count with the following SQL query:
select
d.id,
d.title,
count(distinct o.id)
from store s
left join `order` o on o.store_id = s.id
left join order_product op on op.order_id=o.id
left join store_product sp on sp.id = op.product_id
left join product p on p.id = sp.product_id
left join department_category_to_entity dce1 on dce1.entity_type IN ('Product') and dce1.entity_id = p.id
left join department_category_to_entity dce2 on op.status != 'replaced' and
op.replacement_id is null and
dce2.entity_type IN ('StoreProduct') and
dce2.entity_id = sp.id
left join department_category_to_entity dce3 on op.status = 'replaced' and
op.replacement_id is not null and
dce3.entity_type IN ('StoreProduct') and
dce3.entity_id = op.replacement_id
left join department_category dc on dc.id = p.department_category_id or
dc.id = dce1.category_id or
dc.id = dce2.category_id or
dc.id = dce3.category_id
left join department d on d.id = dc.department_id
where d.id is not null
group by d.id;
Is it possible to get orders count without sub-queries or to get correct count of orders? Please, help... Thank you!
You have LEFT JOIN, which says to keep looking even if there is no row in the 'right' table. But, on the other hand, you are GROUPing BY a column in the last of a chain of LEFT JOINs! Perhaps you meant JOIN instead of LEFT JOIN??
Saying where d.id is not null is roughly equivalent to saying "Oops, all those LEFT JOINs could have been JOINs.
With GROUP BY and JOINs (LEFT or otherwise), you are doing an "inflate-deflate". What logically happens is all the JOINing is done to build a huge intermediate table with all the valid combinations. Then the COUNT(*) and GROUP BY are done. This tends to make the COUNTs (and SUMs, etc) have bigger values than expected.
What's the most direct route to get from department to order? It does not seem to involve store, so get rid of that table.
Are other tables irrelevant?
Even after addressing those issue, you still may be getting the wrong value. Please provide, for starters, `SHOW CREATE TABLE for each table.

show grand children for table person with SQL

I have a table named person(ID,fname,Age,Gender,parentID),
can anyone tell how to show persons who do not have grand childrens in SQL?
This should do the trick, but might be a bit slow if you have a lot of people with a lot of children:
SELECT DISTINCT p.*
FROM person p
LEFT JOIN person c ON c.parentID = p.ID
LEFT JOIN person gc on gc.parentID = c.ID
WHERE gc.ID IS NULL
SELECT *
FROM person
WHERE
ID NOT IN (
SELECT id
FROM (
SELECT p.*,c.id as childID,g.id AS grandChildId
FROM person p
LEFT JOIN person c
ON c.parentID = p.ID
LEFT JOIN person g
ON g.parentID = c.ID
) AS person_relation
WHERE grandChildId
is NOT NULL
Creating temporary tables can be helpful but for a single query this will work .

MySql return empty result when one of table for JOIN is not any data

with below Sql query i can get result successful when all of tables has data, but in this my query when transactions table has not any saved data and its empty my query return empty result, but i want to get null or empty columns data
SELECT transactions.id,
userEwallets.ewalletNumber,
userEwallets.currencySymbol,
transactions.money,
transactions.transactionType,
b.username AS toUser,
a.username AS sender
FROM transactions
JOIN userEwallets ON transactions.ewalletId = userEwallets.id
LEFT JOIN users AS b ON b.id = transactions.toUserId
LEFT JOIN users AS a ON a.id = transactions.fromUserId
WHERE transactions.userId = 37
when its not empty i get this result:
id ewalletNumber currencySymbol money transactionType toUser sender
95 SHIRR9373036569 IRR 20 1 1 amin
you can use a dummy table with one row. The other tables should be left joined to it.
SELECT transactions.id,
userEwallets.ewalletNumber,
userEwallets.currencySymbol,
transactions.money,
transactions.transactionType,
b.username AS toUser,
a.username AS sender
FROM (select 1) dummy
LEFT JOIN transactions ON transactions.userId = 37
LEFT JOIN userEwallets ON transactions.ewalletId = userEwallets.id
LEFT JOIN users AS b ON b.id = transactions.toUserId
LEFT JOIN users AS a ON a.id = transactions.fromUserId
You can allways use a subquery instead of a table name if you give it an allias. Note that you have to move the WHERE condition for the left joined table into the ON clause - Othewise MySQL will convert it to an INNER JOIN.
You are using where condition here. where transactions.userId = 37. If there is no data in transactions table, that where condition will fail and hence returns no data
In order to accomplish that, you should use another table to be the first one. The way you are doing it, if there are no records in the transactions table then there would be nothing to join with.
So, use a table that always has records and LEFT JOIN it with the others.
Try this:
SELECT transactions.id,
userEwallets.ewalletNumber,
userEwallets.currencySymbol,
transactions.money,
transactions.transactionType,
b.username AS toUser,
a.username AS sender
FROM users AS b
LEFT JOIN transactions ON b.id = transactions.toUserId
LEFT JOIN users AS a ON a.id = transactions.fromUserId
JOIN userEwallets ON transactions.ewalletId = userEwallets.id
WHERE transactions.userId = 37

MySql crazy join thorugh a grouping table

I have a database structure with the following setup:
po: id, stockNumber, factoryId, other columns
order: id, stockNumber, factoryId, other columns
stock_number: id, stockNumber, groupId
factory: id, name, groupId
The important part here is the stock_number/factory tables. The groupId column is just an integer and if two or more rows in the table have the same value then their stock numbers/factory are considered the same. Typically this is used for different sizes of the same product.
What I'd like to do is write a query that will join "order" to "po" through the group of stock_number and factory so I can find orders with no matching po. Also the factory has to match the same way.
I have this query if I have a specific stock number/factory in mind but I'd like to update it to query the whole orders table for me:
SELECT id
FROM order
WHERE
styleNumber IN (SELECT a.stockNumber FROM stock_number a INNER JOIN stock_number b ON a.groupId = b.groupId or a.id = b.id WHERE b.stockNumber = '123')
AND factoryId IN (SELECT a.submitter_id FROM submitter a INNER JOIN submitter b ON a.groupId = b.groupId OR a.submitter_id = b.submitter_id WHERE b.SUBMITTER_ID = 'alpha');
EDIT: I came up with this query which I think might be on the right track. It only joins in the stock number so it doesn't do factory yet. Can anyone confirm if I'm going in the correct direction:
SELECT *
FROM order o
LEFT JOIN stock_number s_o ON o.stockNumber = s_o.stockNumber
LEFT JOIN stock_number s_p ON s_o.groupId = s_p.groupId
LEFT JOIN po p ON s_p.stockNumber = p.stockNumber
WHERE p.id IS NULL;
Just join all the tables.
select o.id
FROM order AS o
JOIN stock_number AS sn ON sn.stockNumber = o.stockNumber
JOIN submitter AS su ON ON o.factoryId = su.submitter_id
You could use an anti-join pattern. In this example, it looks complicated because of the two relationship tables. But a query something like this:
SELECT o.id
, o.stockNumber
, o.factoryId
FROM `order` o
LEFT
JOIN `stock_number` s
ON s.stockNumber = o.stockNumber
LEFT
JOIN `factory` f
ON f.id = o.factoryId
AND f.groupId = s.groupId
LEFT
JOIN `po` p
ON p.stockNumber = s.stockNumber
AND p.factoryId = f.id
WHERE p.id IS NULL
The anti-join pattern is easier to visualize with a simpler example. Say you had the order table (as in your example), and an order_line table, with rows related to the order table by the order_id column.
order_line: id, order_id, othercolumns
To get order along with matching order_line rows:
SELECT o.id AS order_id
, l.id AS line_id
FROM `order` o
JOIN `order_line` l
ON l.order_id = o.id
To include rows from order that don't have any matching rows in order_line, we can use an outer join. We add the LEFT keyword:
SELECT o.id AS order_id
, l.id AS line_id
FROM `order` o
LEFT
JOIN `order_line` l
ON l.order_id = o.id
That gets all rows from order, including rows that don't have a matching row in order_line. The trick now is to exclude all the rows that have a matching row. For any rows that didn't have a match, the columns from order_line will be NULL. So we can add a test in the WHERE clause, to exclude rows that had a match.
SELECT o.id AS order_id
, l.id AS line_id
FROM `order` o
LEFT
JOIN `order_line` l
ON l.order_id = o.id
WHERE l.order_id IS NULL
That gets us rows from order that don't have a matching row in order_line.
We can use this same pattern in a more complicated query. We use outer join operations, and rows from order that don't have a matching row in po will have NULL values for the columns from po.

How can I use OR in a left join in MySQL?

Maybe there's a better way to do this... I have a table of member friend requests. The columns are request_id, author_id, recipient_id, status(accepted or denied). I also have a members table whose id is linked to the author or recipient. I want to get a list of a member's friends by selecting from the members table and then joining the requests table. Since the member can be either the author or the recipient of any, some, or none of the requests, a simple LEFT JOIN member_requests AS r ON member_id = r.author_id wouldn't work. How can I write a query that will do this?
SELECT
m.member_id, m.display_name
r.author_id, r.recipient_id, r.status
FROM members AS m
LEFT JOIN member_requests AS r ON m.member_id = r.recipient_id
WHERE r.status = 1 --Accepted
ORDER BY m.display_name
You can use an OR in your left join, like so:
LEFT JOIN member_requests AS r
ON m.member_id = r.recipient_id
OR m.member_id = r.author_id
However, your where clause also needs to be altered:
SELECT
m.member_id, m.display_name
r.author_id, r.recipient_id, r.status
FROM members AS m
LEFT JOIN member_requests AS r
ON (m.member_id = r.recipient_id
OR m.member_id = r.author_id)
AND r.status = 1 //Accepted
ORDER BY m.display_name
When you left join table A to table B, and then specify a restriction in your where clause on table B, you convert your left join into an inner join. Typically, a left join to table B would yield some null values, since table A might have records that don't join to table B. But if you say 'where table B.value = x', you restrict your join to only rows in which table A joins to table B, and furthermore to rows in which 'B.value = x'. The join is then evaluated as an inner join, rather than a left outer.