mysql select from where different - php

I need to select a list of entries, but need to skip those that have matching fields in 2 different tables.
Here's my DB structure
orders:
| orders_id | customers_id |
| 100 | 01 |
| 101 | 20 |
| 102 | 32 |
| 103 | 48 |
| 104 | 99 |
customers (for reference only):
| firstname | lastname | customers_id |
| John | Doe | 20 |
| Fred | Flinty | 22 |
| Mark | Smith | 32 |
testimonials:
| customers_id | testimonial |
| 20 | aaa |
| 32 | bbb |
| 38 | ccc |
| 49 | ddd |
| 55 | eee |
So, I need to select all customers who are in my Orders table, but need to skip them if they are in my Testimonials table. In the example shown above, I would need to select only customers 01, 48 and 99 because they don't exist in Testimonials table.
This is what I tried, but am obviously missing something:
SELECT c.firstname, c.lastname, c.customers_id, o.orders_id,
o.customers_id, s.date_added as status_date
FROM (orders o, testimonials t )
JOIN customers c
ON c.customers_id = o.customers_id
JOIN status_history s
ON s.orders_id = o.orders_id
and s.orders_status_id = o.orders_status
and o.customers_id != t.customer_id
order by o.orders_id ASC;
Can somebody please tell me what I'm doing wrong and how to skip customers that are found in both tables (orders and testimonials)?
I feel I'm on the right track because, if I change the and o.customers_id != t.customer_id to and o.customers_id = t.customer_id I get only the customers that are in both tables (in this case, 20 and 32).

You can LEFT JOIN on this.
The reason for using LEFT JOIN is because it will show all records from the table defined on lefthand side whether it has a matching record or not on the table define on the righthand side. When table Orders is joined with table testimonials, all the records that have no match will have a value of null for the columns in the testimonials table and that's the one you are looking for. To filter out, we only need to get records with NULL value by checking the column with IS NULL.
SELECT a.*, b.*
FROM orders a
LEFT JOIN testimonials c
ON a.customers_ID = c.customers_ID
LEFT JOIN customers b
ON a.customers_ID = b.customers_ID
WHERE c.customers_ID IS NULL
SQLFiddle Demo
SQLFiddle Demo (added some info on the mismatched customer)
To further gain more knowledge about joins, kindly visit the link below:
Visual Representation of SQL Joins
Adding up INDEX.
If on the real database the Orders table as well as Testimonials are always dependent on Customers table, then a FOREIGN KEY constraint should be enforce to preserve referential integrity.
Here's how:
ALTER TABLE Orders ADD CONSTRAINT tb_fk1
FOREIGN KEY (Customers_ID) REFERENCES Customers(Customers_ID);
ALTER TABLE Testimonials ADD CONSTRAINT tb_fk2
FOREIGN KEY (Customers_ID) REFERENCES Customers(Customers_ID);

This is easy way.
select c.* from order as o
join customers as c on o.customers_id = c.customers_id
where o.customers_id not in(select customers_id from testimonials)

Related

Joining table mysql -- without double iteration

Let me start by saying this should be a relatively simple problem which is / was made unnecessary complicated by bad Database design (not by me) that said im also no expert in mysql.
Consider the following
Table Schedule
Note how the columns homeID and visitorID contains the names of the teams and not the actual teamID's
In a bid to fix this I created a new table with columns containing teamID AND teamName as can be seen by below image.
Table Teams
My Problem(s)
I must get the teamID from table Teams for BOTH home team AND away team
So I created the Teams table and this simple script:
SELECT schedule.*, teams.*
FROM schedule
JOIN teams ON schedule.homeID = teams.teamName OR schedule.visitorID = teams.teamName
WHERE schedule.gameID = 411
LIMIT 1 #added Limit1 else the code generates to rows
Output of mysql Script
Limit 1
Notice above how teamID is only generated for 1 team with Limit 1
No Limit Statement (Double Iteration)
Notice above how teamID can get retrieved for BOTH teams. Problem is its doing a double iteration.
TLDR; The above presents the following problems
Firstly the script will generate two outputs one for home team and once for away team. As to be expected however I cant have that.
As a workaround to Problem number 1 -- I added Limit 1 the problem I get with Limit though is that it only gives back a single teamID (as to be expected, I guess)
Question
How can I get BOTH teamID's from table teams with a single iteration? Hope this make sense....
Extra
A demo of application with hard coded team names looks like this (just to give an idea of what they are trying to achieve)
Sounds like you want to join teams twice to schedule.
SELECT s.*,
th.*,
ta.*
FROM schedule s
INNER JOIN teams th
ON s.homeid = th.teamname
INNER JOIN teams ta
ON s.visitorid = ta.teamname
WHERE s.gameid = 411;
I guess that you want to show both team in one row instead of two rows.
If yes, then you need to join the table teams twice.
Consider this demo: http://www.sqlfiddle.com/#!9/bb5e61/1
This join will collect both teams into one row:
SELECT s.*,
t1.teamId as homeId_teamId,
t1.teamCode as homeId_teamCode,
t1.teamName as homeId_teamName,
t2.teamId as visitorId_teamId,
t2.teamCode as visitorId_teamCode,
t2.teamName as visitorId_teamName
FROM Schedule s
JOIN Teams t1 ON s.homeId = t1.teamName
JOIN Teams t2 ON s.visitorId = t2.teamName;
| id | homeId | visitorId | homeId_teamId | homeId_teamCode | homeId_teamName | visitorId_teamId | visitorId_teamCode | visitorId_teamName |
|----|--------|-----------|---------------|-----------------|-----------------|------------------|--------------------|--------------------|
| 1 | Poland | Colombia | 1 | PL | Poland | 2 | CO | Colombia |
However you can also consider LEFT joins instead on INNER joins, which will work in a case where there is no relevant data in the TEAMS table:
SELECT s.*,
t1.teamId as homeId_teamId,
t1.teamCode as homeId_teamCode,
t1.teamName as homeId_teamName,
t2.teamId as visitorId_teamId,
t2.teamCode as visitorId_teamCode,
t2.teamName as visitorId_teamName
FROM Schedule s
LEFT JOIN Teams t1 ON s.homeId = t1.teamName
LEFT JOIN Teams t2 ON s.visitorId = t2.teamName;
| id | homeId | visitorId | homeId_teamId | homeId_teamCode | homeId_teamName | visitorId_teamId | visitorId_teamCode | visitorId_teamName |
|----|----------|-----------|---------------|-----------------|-----------------|------------------|--------------------|--------------------|
| 1 | Poland | Colombia | 1 | PL | Poland | 2 | CO | Colombia |
| 3 | Ya Majka | Poland | (null) | (null) | (null) | 1 | PL | Poland |
| 2 | Ya Majka | Rossija | (null) | (null) | (null) | (null) | (null) | (null) |
Here are the scripts that make up the tables from the examples
CREATE TABLE Schedule(
id int, homeId varchar(20),visitorId varchar(20)
);
INSERT INTO Schedule VALUES
(1, 'Poland', 'Colombia' ),(2,'Ya Majka','Rossija'),
(3,'Ya Majka','Poland');
CREATE TABLE Teams(
teamId int, teamCode varchar(10), teamName varchar(20)
);
INSERT INTO Teams VALUES
(1, 'PL', 'Poland' ),(2,'CO','Colombia'),(3,'US','United States');
You can use a subquery (two of them in the same query) to solve this:
select
gameID,
weekNum,
gameTimeEastern,
(select teamName from teams where teamID = schedule.homeID) as homeName,
homeScore,
(select teamName from teams where teamID = schedule.visitorID) as visitorName,
visitorScore from schedule;
This doesn't get all the columns from schedule, just an example to show how it works. If you need various queries (including select *, though this isn't a good practice except for testing), you could create a view based on a query like the above (with ALL columns from schedule, except homeID and visitorID that get replaced with sub-queries from the teams table). Then you can place queries against that view - and they will work like the original table where you had team names directly in it.

MySQL - inner join - add column with value based on other value

I'm struggling with mysql joins :/
I've multiple tables inside database fe. tasks, users etc.
Table tasks containing tasks with various variables, but the most important - id's of users signed to task (as different roles inside the task - author, graphic, corrector):
+---------+-------------+--------------+
| task_id | task_author | task_graphic |
+---------+-------------+--------------+
| 444 | 1 | 2 |
+---------+-------------+--------------+
Table users
+---------+----------------+------------+-----------+
| user_id | user_nice_name | user_login | user_role |
+---------+----------------+------------+-----------+
| 1 | Nice Name #1 | login1 | 0 |
+---------+----------------+------------+-----------+
| 2 | Bad Name #2 | login2 | 1 |
+---------+----------------+------------+-----------+
Using PDO I'm getting the whole data I want while using INNER JOIN with data from different tables (and $_GET variable)
SELECT tasks.*, types.types_name, warehouse.warehouse_id, warehouse.warehouse_code, warehouse.warehouse_description
FROM tasks
INNER JOIN types ON types.types_id = tasks.task_id
INNER JOIN warehouse ON warehouse.warehouse_id = tasks.task_id
WHERE tasks.task_id = '".$get_id."'
ORDER BY tasks.task_id
Above query returns:
+---------+--------------+--------------+----------------+------------+-----------+------------------+------------------------+------------+-------------+-----------------+-----------+----------------+--------------------+---------------------+-----------+---------------------+------------------+---------------------+
| task_id | task_creator | task_graphic | task_purchaser | task_title | task_lang | task_description | task_description_files | task_files | task_status | task_prod_index | task_type | task_print_run | task_print_company | task_warehouse_code | task_cost | task_time_added | task_deadline | task_date_warehouse |
+---------+--------------+--------------+----------------+------------+-----------+------------------+------------------------+------------+-------------+-----------------+-----------+----------------+--------------------+---------------------+-----------+---------------------+------------------+---------------------+
| 2 | 1 | 2 | 1 | Test | PL | Lorem ipsum (?) | | | w | 2222 | 3 | 456546 | Firma XYZ | 2 | 124 | 29.09.2016 15:48:20 | 01.10.2016 12:00 | 07.10.2016 14:00 |
+---------+--------------+--------------+----------------+------------+-----------+------------------+------------------------+------------+-------------+-----------------+-----------+----------------+--------------------+---------------------+-----------+---------------------+------------------+---------------------+
And I'd like to get query with added user_nice_name after task_creator, task_author and task_graphic - obviously nice names selected from table users based on ID's provide in 3 above fields fe.
+---------+--------------+------------------------------------+--------------+--------------------------------------+
| task_id | task_creator | task_creator_nn | task_graphic | task_graphic |
+---------+--------------+------------------------------------+--------------+--------------------------------------+
| 2 | 1 | Nice Name (from task_creator ID=1) | 2 | Nice Name (from task_graphic ID = 2) |
+---------+--------------+------------------------------------+--------------+--------------------------------------+
How can I achieve that?
You need three joins:
SELECT t.*,
uc.user_nice_name as creator_name,
ug.user_nice_name as graphic_name,
up.user_nice_name as purchaser_name,
ty.types_name, w.warehouse_id, w.warehouse_code, w.warehouse_description
FROM tasks t INNER JOIN
types ty
ON ty.types_id = t.task_id INNER JOIN
warehouse w
ON w.warehouse_id = t.task_id LEFT JOIN
users uc
ON uc.user_id = t.task_creator LEFT JOIN
users ug
ON ug.user_id = t.task_graphic LEFT JOIN
users up
ON up.user_id = t.task_purchaser
WHERE t.task_id = '".$get_id."'
ORDER BY t.task_id;
Notes:
Table aliases make the query easier to write and to read. They are also required because you have three references to users in the FROM clause.
This uses LEFT JOIN for the users in case some of the reference values are missing.
You need to work on your naming. It doesn't make sense that a "warehouse" id matches a "task" id. Or that a "task" id matches a "types" id. But that is how you phrased the query in your question.
The ORDER BY effectively does nothing, because all rows have the same task_id.
Assuming that the task_graphic_name is inside a table name task_graphic_table and the relation field are task_graphic_id
SELECT tasks.*
, types.types_name
, warehouse.warehouse_id
, warehouse.warehouse_code
, warehouse.warehouse_description
, users.user_nice_name
FROM tasks
INNER JOIN types ON types.types_id = tasks.task_id
INNER JOIN warehouse ON warehouse.warehouse_id = tasks.task_id
INNER JOIN users ON users.user_nice_name = tasks.task_graphic
WHERE tasks.task_id = '".$get_id."'
ORDER BY tasks.task_id
And if you need the column appear in a specific order you should explicitally call the column name in sequence eg:
SELECT tasks.col1
, task.col2
, types.types_name
, warehouse.warehouse_id
, warehouse.warehouse_code
, task.col2
, warehouse.warehouse_description
, task_graphic_table.task_graphic_name
Add two sub query in with your query. like
SELECT tasks.*,
....
....,
(select user_nice_name from users where id = tasks.task_author) AS task_creator_name,
(select user_nice_name from users where id = tasks.task_graphic) AS task_graphic_name
FROM tasks
INNER JOIN types ON types.types_id = tasks.task_id
....
....

MySQL query result fluctuating

My problem is more clear on this post: Select all categories with latest post, user, and topic information
——————————————————————————————————————————
I have a query that pulls a list of the categories for my forum along with the latest post in that category. The results come back as expected, except that sub_post information being pulled in the LEFT JOIN fp1 changes if if run the query several times.
I first noticed this problem when viewing my webpage and refreshing several times. The post that it is pulling fluctuates between 3 posts. I'm not sure how this is even possible, unless there is something wrong with the query.
Please, take a look at my query below and let me know if there is something I am doing wrong that might explain this odd behavior.
Cheers.
SELECT fc1.id AS cat_id, fc1.cat_name AS cat_name,
fc1.cat_description AS cat_description, fc1.cat_views as cat_views, fp1.*
FROM forum_categories as fc1
LEFT JOIN (SELECT fp2.id AS sub_post_id,
fp2.post_date as sub_post_date,
fp2.post_topic as sub_post_topic,
u2.id as sub_user_id, u2.username as sub_username,
ft2.topic_subject as sub_topic_subject, ft2.topic_cat as sub_topic_cat
FROM forum_posts as fp2
LEFT JOIN users as u2 on fp2.post_by = u2.id
LEFT JOIN forum_topics as ft2 on ft2.id = fp2.post_topic
LEFT JOIN forum_categories as fcats on fcats.id = ft2.topic_cat
ORDER BY fp2.id DESC)
as fp1 on fp1.sub_topic_cat = fc1.id
GROUP BY fc1.id;
EXPLAIN SELECT:
+----+-------------+------------+--------+-------------------------+-------------+---------+--------------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+--------+-------------------------+-------------+---------+--------------------+------+-------------+
| 1 | PRIMARY | fc1 | index | PRIMARY,cat_name_unique | PRIMARY | 8 | NULL | 3 | Using where |
| 1 | PRIMARY | <derived2> | ref | <auto_key0> | <auto_key0> | 9 | tpw.fc1.id | 9 | NULL |
| 2 | DERIVED | fp2 | index | NULL | PRIMARY | 8 | NULL | 92 | NULL |
| 2 | DERIVED | u2 | eq_ref | PRIMARY | PRIMARY | 8 | tpw.fp2.post_by | 1 | NULL |
| 2 | DERIVED | ft2 | eq_ref | PRIMARY | PRIMARY | 8 | tpw.fp2.post_topic | 1 | NULL |
| 2 | DERIVED | fcats | eq_ref | PRIMARY | PRIMARY | 8 | tpw.ft2.topic_cat | 1 | Using index |
+----+-------------+------------+--------+-------------------------+-------------+---------+--------------------+------+-------------+
I have 3 tables: forums_categories, forums_topics, and forums_posts. I am trying to list the categories along with the latest post in that category. The forums_post is linked to forums_topics by a post_topic and the forums_topics is linked to the forums_categories with a topic_cat.
This was solved by _pala on this other question: https://stackoverflow.com/a/30048334/4864675
I was going about the query wrong which accounted for the odd behavior. Thanks _pala!
Here's the SQL that worked it out for me provided by user _pala:
select fc.cat_name, fc.cat_description, fc.cat_views, u.username, fp.post_date, ft.topic_subject
from forum_categories fc
inner join forum_topics ft
on fc.id = ft.topic_cat
inner join forum_posts fp
on fp.post_topic = ft.id
inner join users u
on fp.post_by = u.id
inner join (
select topic_cat, max(fp.id) most_recent_post
from forum_topics ft
inner join forum_posts fp
on fp.post_topic = ft.id
group by topic_cat
) q
on q.topic_cat = ft.topic_cat
and fp.id = q.most_recent_post;
LEFT JOIN forum_categories as fcats on fcats.id = ft2.topic_cat
I believe the inclusion of that LEFT JOIN has no impact on the result other than to slow down processing. Remove it.
ORDER BY fp2.id DESC
That ORDER BY has no impact on the result because the GROUP BY will not care. Remove it.
If neither of those helps, then explain this:
The post that it is pulling fluctuates between 3 posts.
And please provide EXPLAIN SELECT ...

SQL Complexed Join

I want to list a Purchase Record for a customer by the following tables.
Table: Invoice
----------------------
invID | cusID | total
----------------------
1 | 1 | 10.5
Table: Invoice Item
--------------------
invID | prodID
---------------
1 | 1
1 | 3
Now I want to output as one row Like this: (or in PHP Fetched Row Table)
invID | cusID | prodID | total
-------------------------------
1 | 1 | 1, 3 | 10.5
What have I tried:
SELECT i.*, ii.prodID FROM invoice i, invoiceitem ii WHERE cusID = '1' AND i.invID = ii.invID
Result:
invID | cusID | prodID | total
-------------------------------
1 | 1 | 1 | 10.5
1 | 1 | 3 | 10.5
I think this will work for you. Haven't tested it so there might be a minor typo somewhere, but the concept should work.
SELECT i.invID, i.cusID, GROUP_CONCAT(ii.prodID) `prodID`, i.total
FROM invoice i
INNER JOIN invoiceitem ii ON i.invID = ii.invID
GROUP BY i.invID
You are just missing a GROUP_CONCAT over the prodIDs. You should also use ANSI INNER JOIN syntax, in preference to joining in the WHERE clause. Although MySql doesn't complain, it is also good practice to include all non-aggregated select fields in the GROUP BY, for compliance with other RDBMS's
SELECT i.invID, i.cusID, GROUP_CONCAT(ii.prodID) as prodID, i.total
FROM invoice i INNER JOIN invoiceitem ii ON i.invID = ii.invID
WHERE cusID = '1'
GROUP BY i.invID, i.cusID, i.total;

Joining 3 Tables, mysql php

How do i join all three tables? I have no idea, because i need to call them all into 1 table
customers
+--------+------------+---------------+---------+---------+
| serial | name | email | address | phone |
+--------+------------+---------------+---------+---------+
| 1 | first_name | email#web.com | address | 7777777 |
+--------+------------+---------------+---------+---------+
orders
+--------+------------+------------+
| serial | date | customerid |
+--------+------------+------------+
| 1 | 2014-03-04 | 1 |
+--------+------------+------------+
order_detail
+---------+-----------+----------+-------+
| orderid | productid | quantity | price |
+---------+-----------+----------+-------+
| 1 | 1 | 30 | 400 |
| 1 | 2 | 10 | 500 |
+---------+-----------+----------+-------+
customerid on table orders are the serial on table customers and orderid on table order_detail are the serial on orders
and what if i use another table? for the productid, where productid = product_id in another table?
help would be much appreciated, I am really sorry for the table, i have no idea how to make tables here but they are in order.
You simply need to use Join statements. So you can do following
Select * from Customers
join orders on Customers.serial = orders.customerid
join order_detail on orders.serial = order_detail.orderid
You can also create another table called Product and join the same way as i have showed you.
If you want to select columns from different tables then you have to do like using tablename.columnname. So for example if you want to select quantity and price from order_detail table then do following
Select * from Customers,order_detail.quantity,order_detail.price
join orders on Customers.serial = orders.customerid
join order_detail on orders.serial = order_detail.orderid
Hope you got my point.

Categories