MySQL INNER JOIN USE - mysql

I have two tables:
+-----------------------+
| Tables_in_my_database |
+-----------------------+
| orders |
| orderTaken |
+-----------------------+
In orders, there are attributes like orderId, orderName.
In orderTaken, there are attributes like userId, orderId and orderStatus.
Basically, the mechanism of my project is running like:
A user with a unique userId will be able to take an order from the web page, where each order has its own unique orderId as well. After taken, the orderTaken table will record the userId, orderId and initially set orderStatus = 1.
Now, I want to select the orders taken by one user where the orderStatus = 1, but also I will need the orderName from my orders table to be shown. I've learned inner join and written a query as this:
SELECT * FROM orders
WHERE orders.orderId IN
(SELECT orderId FROM orderTaken
WHERE orderTaken.userId = '8' AND orderTaken.orderStatus = '1')
INNER JOIN orderTaken ON orders.orderId = orderTaken.orderId
However, MySQL keeps complaining about syntax error. I guess I cannot use inner join this way? Can anybody correct me? Thanks!

SELECT * FROM orders
INNER JOIN orderTaken ON orders.orderId = orderTaken.orderId
WHERE orderTaken.userId = '8' AND orderTaken.orderStatus = '1'
You have over complicated it. This will work.
I have left the quotes in for the userId since I assumed that's right, but if they are of data type INT then you should remove them.
If you want some reading on JOINs then see these links
Join terminology: inner, outer, semi, anti
W3Schools

Try like this
SELECT *
FROM (orders INNER JOIN ordersTaken ON orders.orderId = orderTaken.orderId)
WHERE orderId IN
(SELECT orderId
FROM orderTaken
WHERE orderTaken.userId = '8' AND orderTaken.orderStatus = '1')

Related

Filtering on a LEFT JOINED column

Is there a more efficient way to filter on a joined table as in the following example? Or is this a fine approach? This query returns the desired results, but I am an amateur at MySQL.
I have indexes on products.id, product_details.product_id and product_details.value
SELECT p.id
FROM products p
LEFT
JOIN product_details d
ON d.product_id = p.id
WHERE d.value = 1
OR p.id = 4
Simplified structure as follows:
products table
product_id (PRIMARY KEY) | name
--------------------------------
1 | Shirt
2 | Shoes
3 | Dress
4 | A product with no corresponding details row
product_details table
product_id (PRIMARY KEY) | value
---------------------------------
1 | 1
2 | 23
3 | 32
This is your query:
SELECT products.id
FROM products LEFT JOIN
product_details
ON product_details.product_id = products.id
WHERE product_details.value = 1 OR products.id = 4;
This is not a bad practice. I do think the query is easier to follow using EXISTS:
SELECT p.id
FROM products p
WHERE p.id = 4 OR
EXISTS (SELECT 1
FROM product_details pd
WHERE pd.product_id = p.id AND pd.value = 1
);
In addition EXISTS makes it clear that you don't want to return duplicates if there are duplicate matching rows in product_details.
If performance is you main consideration, then EXISTS is probably your best choice, with an index on product_details(product_id, value).
Couple of notes:
As a rule of thumb, a UNION ALL statement performs better than an OR operator. Also, this helps clear up the query.
Using both an implicit JOIN and a predicate in the WHERE clause on the same table can get you into trouble - especially if you're using a LEFT OUTER JOIN (the predicate in the WHERE clause has precedence over the LEFT OUTER JOIN).
Seems like you always want to pull back any records that has a products.id = 4, and also any products that have a product_details.value = 1. This seems like two separate queries to me, and splitting it would probably make it easier to maintain in the future.
SELECT
p.id
FROM
products p
WHERE
p.id = 4
UNION ALL
SELECT
p.id
FROM
product_details pd
JOIN
products p
ON
p.id = pd.product_id
WHERE
pd.value = 1
Source: https://bertwagner.com/posts/or-vs-union-all-is-one-better-for-performance/

"Combine" FULL OUTER JOIN and NOT IN?

I have two tables:
+-----------------------+
| Tables_in_my_database |
+-----------------------+
| orders |
| orderTaken |
+-----------------------+
In orders, there are attributes
orderId, orderName, isClosed and orderCreationTime.
In orderTaken, there are attributes
userId, orderId and orderStatus.
Let's say when
orderStatus = 1 --> the customer has taken the order
orderStatus = 2 --> the order has been shipped
orderStatus = 3 --> the order is completed
orderStatus = 4 --> the order is canceled
orderStatus = 5 --> the order has an exception
Basically the mechanism of my project is running like: A user with a unique userId will be able to take an order from the web page, where each order has its own unique orderId as well. After taken, the orderTaken table will record the userId, orderId and initially set orderStatus = 1. The shop then update the orderStatus based on various situations. Once the shop has updated isClosed = 1 then this order wouldn't be displayed at all no matter the user has taken it or not(not make sense but it's just a isClosed == 0 in the query).
Now, I want to construct a web page that will show both the new orders that the user hasn't taken yet (which should be the orders that their orderIds are not recorded in the orderTaken table under this user's userId), and the orders that the user has already taken with the orderStatus shown BUT the orderStatus IS NOT 4 or 5, group by orderCreationTime DESC (yea, maybe not make sense if I don't have a orderTakenTime but let's keep it that way), like:
OrderId 4
Order Name: PetPikachu
orderStatus = 1
CreationTime: 5am
OrderId 3
Order Name: A truck of hamsters
orderStatus = 3
CreationTime: 4am
OrderId 2
New order
Order Name: Macbuk bull
CreationTime: 3am
OrderId 1
Order Name: Jay Chou's Album
orderStatus = 2
CreationTime: 2am
I have this query written based on the knowledge I've learned:
SELECT * FROM orders A WHERE A.isClosed == '0' FULL OUTER JOIN orderTaken B WHERE B.userId = '4' AND (B.orderStatus<>'4' OR B.orderStatus<>'5') ORDER BY A.orderCreationTime DESC;
Apparently this query doesn't work, but I'm afraid to have a
ON A.orderId = B.orderId
since then the table returned will eliminate the new orders that the orderId hasn't been recorded in orderTaken B. I've also tried a NOT IN clause like
SELECT * FROM orders A WHERE A.isClosed = '0' AND A.orderId NOT IN (SELECT orderId FROM orderTaken B WHERE B.userId = '$userId' AND (B.orderStatus='4' OR B.orderStatus='5')) ORDER BY creationTime DESC;
This query works but it doesn't have the field orderStatus from orderTaken B in the returned table. I was thinking to add another JOIN orderTaken B clause after this query to get the fields from B but I think that's not a good way to write a query.
I just wanna kinda combine "NOT IN" and "FULL JOIN". Can anybody help me out? Thanks!
Just like #terje-d said, what you need is LEFT JOIN. Updated it with the the original table names and fixed the $userId filter.
For all open orders and incomplete orders.
SELECT o.`orderId`,
o.`orderName`,
ot.`orderStatus`,
o.`orderCreationTime`
FROM orders o
LEFT JOIN orderTaken ot
ON o.orderId = ot.orderId
WHERE o.isClosed = 0
AND (
ot.orderId IS NULL
OR ot.orderStatus NOT IN (4,5)
)
ORDER BY o.`orderCreationTime` DESC
For all open orders and incomplete orders for a particular user
SELECT o.`orderId`,
o.`orderName`,
ot.`orderStatus`,
o.`orderCreationTime`
FROM orders o
LEFT JOIN orderTaken ot
ON o.orderId = ot.orderId
WHERE o.isClosed = 0
AND ( ot.orderStatus IS NULL
OR (
ot.user_id = ?
AND ot.orderStatus NOT IN (4,5)
)
)
ORDER BY o.`orderCreationTime` DESC
You seem to want to find the records in orders that is not assigned to an user (i.e. does not have a related record in orderTaken) plus the ones that are assigned to an user, but where the orderStatus is not 4 or 5.
Then a full outer join is not needed as there will be no records in orderTaken without a related record in orders. A Left inner join can be used to find all the records from orders, an onclause will include data from the related records from orderTaken and the where clause can then filter out orders taken by other users, or where orderStatus is 4 or 5:
SELECT o.*, ot.userID, ot.orderStatus
FROM orders o
LEFT JOIN orderTaken ot
ON ot.orderID = o.orderID
WHERE o.isClosed = 0
AND (ot.userID IS NULL OR ot.userID = $userID AND ot.orderStatus NOT IN (4,5))
ORDER BY o.creationTime DESC

How to select users without specific one to many rows in MySQL

Consider the following data set:
users table:
id (int) email (string)
1 first#example.com
2 second#example.com
order_items table:
id (int) user_id (int) generation (string)
1 1 '11'
2 1 '12'
2 1 '12.50'
3 1 '16.00'
4 2 '11'
5 2 '12'
UPDATED question
How can I select users which doesn't have order_items with generation 16.00 and have at least one order_item?
So:
email
second#example.com
1) Returning Users who don't have order item with generation 16 included users with no orders at all.
Assuming you have some kind of id column in order_items table:
select u.* from users u
left outer join order_items oi on (u.id = oi.user_id and oi.generation = 16)
where oi.id is null;
Otherwise use whatever primary key you have in order_items in the where condition to be NULL.
Updated to include answer for the question in comment
2) Returning users who don't have order item with generation 16 but have least one order.
select distinct u.* from users u
left outer join order_items oi16 on (u.id = oi.user_id and oi.generation = 16)
join order_items oiother on (u.id = oiother.user_id and oiother.generation != 16)
where oi16.id is null;
We do the filtering by using a second (normal) join which only returns users where it finds matching rows from the order_items table.
Here we need the distinct because the second join will multiply your rows depending on how many other orders the user have.
Alternatively you can also do a count or sum like this:
select u.*, count(distinct oiother.id) from users u
left outer join order_items oi16 on (u.id = oi.user_id and oi.generation = 16)
join order_items oiother on (u.id = oiother.user_id and oiother.generation != 16)
where oi16.id is null
group by u.id;
This will give you also how many other order items each returned user have. Or omit the count completely and using group by just to return distinct items.
You can use NOT EXISTS() like this:
SELECT * FROM Users u
WHERE NOT EXISTS(SELECT 1 FROM order_items o
WHERE o.userid = u.id
AND o.generation = 16)
That checks if there is a record for this user with order.generation = 16, and if there isn't it selects him.
Or not in()
SELECT * FROM Users u
WHERE u.id NOT IN(SELECT userid FROM order_items o
WHERE o.generation = 16)
That selects the list of users who have order.generation = 16, and select every id except them.
Following query should give you the desired output:
*update*
changed query as per the new result format in the question
As we want the data only from generation table, join with user table is not needed anymore. Here's the updated query:
select id, generation
from mytable where id not in (
select id from mytable
where generation = 16
group by id
);
Here is the SQL fiddle for it.

Joining two tables for the result?

I have 2 tables in a database person and order tables.
PERSON table:
PERSON_ID | NAME
ORDER table:
ORDER_ID | ORDER_NO | PERSON_ID
I need to display all the orders + a name of corresponding person if it exists, if not just order details.
So far I got up to query:
SELECT ORDER_ID, ORDER_NO, order.PERSON_ID, NAME
FROM person, order
WHERE person.PERSON_ID = order.PERSON_ID AND
person.FIRST_NAME IS NOT NULL;
Which gives me orders only if the name is available whereas I need to display all the orders despite the fact if name is available or not.
Any suggestions?
Yes, you can use LEFT JOIN for that:
SELECT o.order_id, o.order_no, o.person_id, p.name
FROM `order` o
LEFT JOIN person p
ON p.person_id = o.person_id AND p.FIRST_NAME IS NOT NULL
With LEFT JOIN if the name is null it will still give you the orders.

mySQL Query JOIN in same table

Table structure goes something like this:
Table: Purchasers Columns: id | organization | city | state
Table: Events Columns: id | purchaser_id
My query:
SELECT purchasers.*, events.id AS event_id
FROM purchasers
INNER JOIN events ON events.purchaser_id = purchasers.id
WHERE purchasers.id = '$id'
What I would like to do, is obviously to select entries by their id from the purchasers table and join from events. That's the easy part. I can also easily to another query to get other purchasers with the same organization, city and state (there are multiples) but I'd like to do it all in the same query. Is there a way I can do this?
In short, grab purchasers by their ID but then also select other purchasers that have the same organization, city and state.
Thanks.
You could try something like
SELECT p.*,
e.id
FROM purchasers p INNER JOIN
events e ON e.purchaser_id = p.id INNER JOIN
(
SELECT p.*
FROM purchasers p
WHERE p.id = '$id'
) Original ON p.organization = Original.organization
AND p.city = Original.city
AND p.state = Original.state
The subselect Original will return the original purchaser, and then link to the purchasers table by organization, city and state
EDIT:
Changed the query, this will still return duplicates, but only for the number of events registered per purchaser. If you wish to retrieve a DISTINCT list of purchasers, you cannot do this with the event id, so you need something like
SELECT p.*
FROM purchasers p INNER JOIN
(
SELECT p.*
FROM purchasers p
WHERE p.id = '$id'
) Original ON p.organization = Original.organization
AND p.city = Original.city
AND p.state = Original.state