Optimize SQL query to fetch file names - php

I've two tables, the first table contains information on the ideas submitted by user and the second table contains information on the file attachments that are part of the idea. An idea submitted by the user can have 0 or any number of attachments.
Table 1:
-------------------------------------
Id Title Content Originator
-------------------------------------
1 aaa bbb John
2 ccc ddd Peter
--------------------------------------
Table 2:
---------------------------------------------
Id Idea_id Attachment_name
---------------------------------------------
1 1 file1.doc
2 1 file2.doc
3 1 file3.doc
4 2 user2.doc
---------------------------------------------
Table 1 primary key is Id and table 2 primary key is Id as well. Idea_id is the foreign key in table 2 mapping to table 1 Id.
I'm trying to display all the ideas, along with their attachments in a html page. So what I've been doing is: get all the ideas from Table 1 and then for each idea record, retrieve the attachment records from table 2.It seems to be extremely inefficient. Could this be optimized so that I can retrieve idea records and their corresponding attachment records in one query?
I tried with left outer join(Table 1 left outer join Table 2) but that would give me three records for Id = 1 in table 1. I'm looking for a SQL query to club idea detail and attachment names in 1 row to make HTML page processing efficient. Otherwise, What would be the best solution for this?

If you want to get all attachments along with all ideas, you may use GROUP_CONCAT. such as
SELECT *, (SELECT GROUP_CONCAT(attachment_name separator ', ') FROM TABLE2 WHERE idea_id = TABLE1.id) attachments FROM TABLE1

I probably missed the point but a left join should bring back all the records
create table `ideas` (
`id` int(10) unsigned not null auto_increment,
`title` varchar(50) not null,
`content` varchar(50) not null,
`originator` varchar(50) not null,
primary key (`id`)
)
engine=innodb
auto_increment=3;
create table `attachments` (
`id` int(10) unsigned not null auto_increment,
`idea_id` int(10) unsigned not null default '0',
`attachment` varchar(50) not null default '0',
primary key (`id`),
index `idea_id` (`idea_id`),
constraint `fk_ideas` foreign key (`idea_id`) references `ideas` (`id`) on update cascade on delete cascade
)
engine=innodb
auto_increment=5;
mysql> select * from ideas;
+----+----------------+-----------+-----------------+
| id | title | content | originator |
+----+----------------+-----------+-----------------+
| 1 | Flux capacitor | Rubbish | Doc |
| 2 | Star Drive | Plutonium | Professor Frink |
+----+----------------+-----------+-----------------+
mysql> select * from attachments;
+----+---------+------------------------------+
| id | idea_id | attachment |
+----+---------+------------------------------+
| 1 | 1 | Flux capacitor schematic.jpg |
| 2 | 1 | Sensors.docx |
| 3 | 1 | fuel.docx |
| 4 | 2 | plans.jpg |
+----+---------+------------------------------+
mysql> select * from ideas i
-> left outer join attachments a on a.idea_id=i.id;
+----+----------------+-----------+-----------------+------+---------+------------------------------+
| id | title | content | originator | id | idea_id | attachment |
+----+----------------+-----------+-----------------+------+---------+------------------------------+
| 1 | Flux capacitor | Rubbish | Doc | 1 | 1 | Flux capacitor schematic.jpg |
| 1 | Flux capacitor | Rubbish | Doc | 2 | 1 | Sensors.docx |
| 1 | Flux capacitor | Rubbish | Doc | 3 | 1 | fuel.docx |
| 2 | Star Drive | Plutonium | Professor Frink | 4 | 2 | plans.jpg |
+----+----------------+-----------+-----------------+------+---------+------------------------------+

Related

Confusion between PK and FK table design

I have a person table and a score table. The Person table basically stores a person's information while score table stores what kind of score a person has. I set the FK constraint in score table to ON DELETE: CASCADE
person
- id
- name
- scored_id (FK)
score
- id (PK)
- bmi
- weight
So, in the table setting score.id is linked with person's scored_id. That being said when I delete a record in score, a person will get deleted as well. But why when I delete a record in person, the record of his in score is not deleted?
Just an idea how you might structure the tables and use a foreign key which will delete records from the score table if/when a user from the person table is deleted. The score table should have a reference to the user - pid which is used as the foreign key dependancy. It makes sense to me that the score is dependant upon the user so no user, no score.
create table `person` (
`id` int(10) unsigned not null auto_increment,
`name` varchar(50) null default null,
primary key (`id`)
)
collate='latin1_swedish_ci'
engine=innodb
auto_increment=4;
mysql> describe person;
+-------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| name | varchar(50) | YES | | NULL | |
+-------+------------------+------+-----+---------+----------------+
create table `score` (
`id` int(10) unsigned not null auto_increment,
`bmi` int(10) unsigned not null default '0',
`weight` int(10) unsigned not null default '0',
`pid` int(10) unsigned not null default '0',
primary key (`id`),
index `pid` (`pid`),
constraint `fk_sc_pid` foreign key (`pid`) references `person` (`id`) on update cascade on delete cascade
)
collate='latin1_swedish_ci'
engine=innodb
auto_increment=4;
mysql> describe score;
+--------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| bmi | int(10) unsigned | NO | | 0 | |
| weight | int(10) unsigned | NO | | 0 | |
| pid | int(10) unsigned | NO | MUL | 0 | |
+--------+------------------+------+-----+---------+----------------+
mysql> select * from person;
+----+------+
| id | name |
+----+------+
| 1 | bob |
| 2 | rita |
| 3 | sue |
+----+------+
mysql> select * from score;
+----+-----+--------+-----+
| id | bmi | weight | pid |
+----+-----+--------+-----+
| 1 | 34 | 34 | 1 |
| 2 | 56 | 41 | 2 |
| 3 | 56 | 77 | 3 |
+----+-----+--------+-----+
mysql> delete from person where id=3;
Query OK, 1 row affected (0.00 sec)
/* delete a user, the score disappears too which makes sense */
mysql> select * from score;
+----+-----+--------+-----+
| id | bmi | weight | pid |
+----+-----+--------+-----+
| 1 | 34 | 34 | 1 |
| 2 | 56 | 41 | 2 |
+----+-----+--------+-----+
Your issue is semantic understanding of the task, rather than syntax. Intuitively your relation looks wrong. It is unlikely, that a particular score, say 75kg and bmi of 20 will need to have a many relations link to people with the same score. This would be arbitary. More likely, your want, a person to have different scores over time, then when you delete a person, you want their associated values deleted. So table relation should be:
person
- id (Primary Key)
- name
score
- id (Primary Key)
- bmi
- weight
- scoreDate
- personID (Foreign Key to person)
A score date would be a helpful addition.
This structure will allow a person to have a history of many score and see the fluctuation of their weight and body mass index over time. A semantically helpful task that resonates with reality, and therefore follows the notions of entity analysis and table structures following the real world application.
Helpful discussion of ERD and table structure levels and relations
In you tables, "person" table is having reference(FK) of "score" table so when you delete a record in "score" table mysql search related record in "users" table to delete.
but "score" table dose not have any reference(FK) of "person" table.
You can try below table structure if you want to delete score record when person record will be delete but person record will be still safe if score record will be delete
person
- id (PK)
- name
score
- id (PK)
- person_id (FK)
- bmi
- weight

Right query to limit results in search

My db structure is as follows: categories table
+-----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| parent_id | int(11) | NO | | NULL | |
| title | varchar(260) | NO | | NULL | |
| slug | varchar(260) | NO | | NULL | |
| custom | int(11) | NO | | NULL | |
+-----------+--------------+------+-----+---------+----------------+
category_to_content table
+-------------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+---------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| category_id | int(11) | NO | | NULL | |
| content_id | int(11) | NO | | NULL | |
+-------------+---------+------+-----+---------+----------------+
And content table:
+--------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| heading | varchar(200) | NO | | NULL | |
| subheading | text | NO | | NULL | |
| content_text | longtext | NO | | NULL | |
+--------------+--------------+------+-----+---------+----------------+
As you can see, typical adjacency list model in categories table, category_to_content is here because some categories share same content...
There are 4 levels currently, and 'level 0' categories are actually big/main categories (infobases in this case), and i want to limit search JUST to (sub)categories/content which belongs to: current level 0 category (parent_id of level 0 categories is, well.. 0).
I'm using Codeigniter, query builder class (not mandatory, i can use plain sql query, too, if needed), and returned query looks like this:
SELECT `heading`, `slug`
FROM `content`
JOIN `category_to_content` ON `category_to_content`.`content_id` = `content`.`id`
JOIN `categories` ON `categories`.`id`=`category_to_content`.`category_id`
WHERE `categories`.`id` IN('1,2,3') // current level 0 category //subcategories id's, 12,3, just as example...
AND (
`content`.`heading` LIKE '%pod%'
OR `content`.`subheading` LIKE '%pod%'
)
LIMIT 10
However, this doesn't return desired results in all cases. If i remove parentheses from AND portion of query, i got results, BUT from all main categories. With parentheses, no results, in most cases...
E.g. when search term is 'about' i got proper results, when i try with 'podiatry' - no results.
Sample data:
Just to clarify - IN portion works fine, i got proper level 0 subcategories ids on every page.
If you can't see my error (probably something trivial for MySQL gurus), i can send sample data (export from PhPMyAdmin).
EDIT1: Example: When i type 'pod' i would like to get slug from categories table -> 'podiatry', and heading from 'content' table - 'What is podiatry'.
Interesting, when i type 'about' i get desired results (from desired level 0 SUBcategory). DB export - ASAP, so you can test by your self...
EDIT2: link to sample data: http://pastebin.com/YHziZH8f
EDIT3: in this case, i made 3 main/level0 categories (podiatry - cat id=1, infobase2, cat id=208, test3, cat id=213). They all (almost) have some subcategories, and content related to them. If we are on 'podiatry' page - current level 0 category subcategories should be searched. I want to limit search just to these subcategories (current level 0 subcategories). So, if i am on 'podiatry' page, i need 'about' content just for podiatry category (no 'about' from/for the rest of categories).
EDIT4:
Example of successful query:
SELECT `heading`, `slug`
FROM `content`
JOIN `category_to_content` ON `category_to_content`.`content_id` = `content`.`id`
JOIN `categories` ON `categories`.`id`=`category_to_content`.`category_id`
WHERE `categories`.`id` IN('209,210,211,212')
AND (
`content`.`heading` LIKE '%about%'
OR `content`.`subheading` LIKE '%about%'
)
LIMIT 10
I got: heading 'About Infobase2', and 'about-infobase2' slug.
Second search term:
SELECT `heading`, `slug`
FROM `content`
JOIN `category_to_content` ON `category_to_content`.`content_id` = `content`.`id`
JOIN `categories` ON `categories`.`id`=`category_to_content`.`category_id`
WHERE `categories`.`id` IN('209,210,211,212')
AND (
`content`.`heading` LIKE '%categ%'
OR `content`.`subheading` LIKE '%categ%'
)
LIMIT 10
No results. If you test sample data you will see that there is Category 2 (id=210) in categories table, referenced as: category_id 210, content id 48, in category_to_content table, and Category 2 heading in content table (id=48)... What i'm doing wrong... I can't get that data. :(
Try this: first I join all tables needed
SELECT content.heading, categories.slug
FROM content
INNER JOIN category_to_content ctc ON ctc.content_id = content.id
INNER JOIN categories ON categories.id = ctc.category_id
Then we need to keep only "level 1 categories" (direct subcategories of level 0 categories): I used a subquery instead of listing needed ids. It's more dynamic that way: if you add new categories, no need to update the query.
WHERE categories.parent_id IN (SELECT id FROM categories WHERE parent_id = 0)
Finally, search by keyword.
AND (content.heading LIKE '%pod%' OR content.subheading LIKE '%pod%')
(I'm not sure wether you want to add OR categories.slug LIKE '%pod%' in that part of your query or not.)
And you can add LIMIT 10 at the end if needed.
A word about what was wrong in your query, it's this line:
WHERE `categories`.`id` IN('1,2,3')
it should have been IN(1,2,3) or IN('1','2','3') (see the quotes)
"level 1" categories are not only 1, 2 and 3

Appending matching column onto each result row

I'm not a SQL veteran so please excuse me if this is obvious; I'm learning.
I have two tables in a database for a wedding invite system; guests and invites. One invite (invitation) can contain many guests.
For purposes of creating a mail merge for the invites, I'm trying to select the firstname and lastname from the guest table, where the guest's inviteID is the same as the others; effectively returning on row of data containing the inviteID's data and a column each for the names of the guests.
My problem is I can return the data, but across multiple rows which won't work for the mail-merge. I can create a PHP script to do a work-around, but I would like to learn how this could be achieved in pure SQL.
Can anybody shed some light? Can this be done? Is this sheer madness?
Hoping to achieve:
*************************** 1. row ***************************
inviteID: 39
inviteURLSlug: thewinnetts
....
guestFirstName1: Sid
guestSurname1: Winnett
guestFirstName2: Claire
guestSurname2: Winnett
'invite' table:
+---------------------+--------------+
| Field | Type |
+---------------------+--------------+
| inviteID | int(11) |
| inviteURLSlug | varchar(64) |
| inviteQRValue | varchar(255) |
| inviteQRImageURL | varchar(255) |
| inviteAddress1 | varchar(32) |
| inviteAddress2 | varchar(32) |
| inviteAddress3 | varchar(32) |
| inviteCity | varchar(32) |
| inviteCounty | varchar(32) |
| inviteCountry | varchar(32) |
| invitePostcode | varchar(16) |
| inviteDateSend | datetime |
| inviteDateResponded | datetime |
| inviteCreated | datetime |
| inviteUpdated | timestamp |
+---------------------+--------------+
'guest' table:
+-------------------+--------------+
| Field | Type |
+-------------------+--------------+
| guestID | int(11) |
| inviteID | int(11) |
| guestFirstName | varchar(32) |
| guestSurname | varchar(32) |
| guestSide | varchar(8) |
| guestAttending | tinyint(1) |
| guestEmail | varchar(255) |
| guestPhone | varchar(32) |
| guestMobile | varchar(16) |
| guestAddress1 | varchar(32) |
| guestAddress2 | varchar(32) |
| guestAddress3 | varchar(32) |
| guestCity | varchar(32) |
| guestCounty | varchar(32) |
| guestCountry | varchar(32) |
| guestPostCode | varchar(16) |
| guestProfilePhoto | varchar(64) |
| guestFoodVeg | tinyint(1) |
| guestFoodReq | varchar(255) |
| guestTwitter | varchar(15) |
| guestFacebook | varchar(32) |
| guestPlusone | int(1) |
| guestCreated | datetime |
| guestUpdated | timestamp |
+-------------------+--------------+
Failed Join attempt and cropped results sample:
SELECT * FROM guest INNER JOIN invite on guest.inviteID = invite.inviteID \G
*************************** 64. row ***************************
guestID: 72
inviteID: 39
guestFirstName: Claire
guestSurname: Winnett
.......
*************************** 65. row ***************************
guestID: 73
inviteID: 39
guestFirstName: Sid
guestSurname: Winnett
.......
To achieve those results will require additional processing in PHP.
To do so, modify your query to this:
SELECT i.*,
g.guestFirstName,
g.guestSurname
GROUP_CONCAT(DISTINCT g.guestFirstName, '|', g.guestSurname ORDER BY g.guestSurname DESC) AS names
FROM invite i
INNER JOIN guest g ON i.inviteID = g.inviteID;
making sure to return your results as an array. From there, convert the concatenated results into an array then iterate through them to set the custom key name and corresponding value (assumes your query results are in array format with the variable name $queryresults):
$names = explode(',' $queryresults['names']);
unset($queryresults['names']);
$i = 1;
foreach ($names as $name) {
$split_name = str_split("|", $name);
$queryresults['guestFirstName' . $i] = $split_name[0];
$queryresults['guestSurname' . $i] = $split_name[1];
$i++;
}
This should give you your desired results.
SQL is not good at this sort of thing. It wants to work in sets, where all the items in the set are the same. You are asking for two items within the set to be smooshed together. Here is a possible solution:
SELECT
t1.inviteID,
t1.guestID,
t1.guestFirstName,
t1.guestSurname,
t2.guestFirstName AS guestFirstName2,
t2.guestSurname AS guestSurname2
FROM
guests as t1
LEFT OUTER JOIN
guests as t2
ON t1.inviteID = t2.inviteID
AND t1.guestID <> t2.guestID
WHERE
t1.guestID = (select min(t3.guestID) from guests as t3 where t3.inviteID = t1.inviteID)
;
The guests table is used twice, once to provide the first guest on each invite, and a second time, using a LEFT OUTER JOIN to provide the second. The LEFT OUTER ensures you still get the first even if there isn't a second. The other criteria are there to ensure you don't join a row to itself, and you only output the firsts, with the seconds attached (and not the other way around).
Here is a sample fiddle (in MySQL)
Is something like this what you are looking for?
Updated after your comment.
SELECT GROUP_CONCAT( DISTINCT `guest`.`guestFirstName`,`guest`.`guestSurname`, `invite`.`inviteURLSlug` ) FROM `guest`
LEFT JOIN `invite` ON `invite`.`inviteID` = `guest`.`inviteID`
WHERE `invite`.`inviteID` = 5
I just made two simple tables with minimal information for testing, but is easily expandable to whatever you have/want.
You are going to have some fields repeated since the number of rows is going to be related to the number of guests.

Multi table deletion MySQL through PHP

I am making a simple database to use as a mock example for an E-Commerce website. One things that I am required to do it delete records from the database using PHP. I can delete a user name or a order or etc.. But when I try to do them all at once I am screwing something up. What I want to happen is delete all the information about a user. Example : User info, Shipping Info, Billing Info, Credit Card Info, and anything else to do with the specific user. My tables are as followed.
mysql> SHOW columns FROM shirt_billing_addresses;
+----------------------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------------------+--------------+------+-----+---------+----------------+
| shirt_billing_addresses_id | mediumint(9) | NO | PRI | NULL | auto_increment |
| house | mediumint(9) | NO | | NULL | |
| street | varchar(100) | NO | | NULL | |
| city | varchar(100) | NO | | NULL | |
| state | char(2) | NO | | NULL | |
| zip | char(5) | NO | | NULL | |
+----------------------------+--------------+------+-----+---------+----------------+
6 rows in set (0.01 sec)
mysql> SHOW columns FROM shirt_credit_cards;
+-----------------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------------------+--------------+------+-----+---------+----------------+
| shirt_credit_cards_id | mediumint(9) | NO | PRI | NULL | auto_increment |
| shirt_users_id | mediumint(9) | NO | MUL | NULL | |
| type | varchar(30) | NO | | NULL | |
| no | char(16) | NO | | NULL | |
| security_code | char(3) | NO | | NULL | |
+-----------------------+--------------+------+-----+---------+----------------+
5 rows in set (0.04 sec)
mysql> SHOW columns FROM shirt_orders;
+-----------------------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------------------------+--------------+------+-----+---------+----------------+
| shirt_orders_id | int(11) | NO | PRI | NULL | auto_increment
| order_total | double(6,2) | NO | | NULL | |
| payment_date | datetime | NO | | NULL | |
| shirt_credit_cards_id | mediumint(9) | NO | MUL | NULL | |
| shirt_shipping_addresses_id | mediumint(9) | NO | MUL | NULL
| shirt_billing_addresses_id | mediumint(9) | NO | MUL | NULL |
| shirt_shipping_methods_id | tinyint(4) | NO | MUL | NULL |
+-----------------------------+--------------+------+-----+---------+----------------+
7 rows in set (0.02 sec)
mysql> SHOW columns FROM shirt_shipping_addresses;
+-----------------------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------------------------+--------------+------+-----+---------+----------------+
| shirt_shipping_addresses_id | mediumint(9) | NO | PRI | NULL | auto_increment
| house | mediumint(9) | NO | | NULL | |
| street | varchar(100) | NO | | NULL | |
| city | varchar(100) | NO | | NULL | |
| state | char(2) | NO | | NULL | |
| zip | char(5) | NO | | NULL | |
+-----------------------------+--------------+------+-----+---------+----------------+
6 rows in set (0.03 sec)
mysql> SHOW columns FROM shirt_users;
+----------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------+--------------+------+-----+---------+----------------+
| shirt_users_id | mediumint(9) | NO | PRI | NULL | auto_increment |
| first_name | varchar(30) | NO | | NULL | |
| last_name | varchar(30) | NO | | NULL | |
| email | varchar(30) | NO | | NULL | |
| user_id | varchar(30) | NO | | NULL | |
| password | char(40) | NO | | NULL | |
+----------------+--------------+------+-----+---------+----------------+
6 rows in set (0.03 sec)
mysql> SHOW columns FROM shirt_users_types;
+----------------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------------+--------------+------+-----+---------+----------------+
| shirt_users_types_id | int(11) | NO | PRI | NULL | auto_increment |
| shirt_users_id | mediumint(9) | NO | MUL | NULL | |
| shirt_types_id | smallint(6) | NO | MUL | NULL | |
| shirt_orders_id | int(11) | NO | MUL | NULL | |
| type_quantity | smallint(6) | NO | | NULL | |
| type_total | double(6,2) | NO | | NULL | |
+----------------------+--------------+------+-----+---------+----------------+
6 rows in set (0.03 sec)
The PHP code I have is as follows:
#mysqli_query($link, "SET AUTOCOMMIT=0");
$select_sui = "SELECT
shirt_users.shirt_users_id,
shirt_users_types.shirt_users_types_id,
shirt_orders.shirt_orders_id,
shirt_shipping_addresses.shirt_shipping_addresses_id,
shirt_billing_addresses.shirt_billing_addresses_id,
shirt_credit_cards.shirt_credit_cards_id
from
shirt_users,
shirt_users_types,
shirt_orders,
shirt_shipping_addresses,
shirt_billing_addresses,
shirt_credit_cards
where
shirt_users.shirt_users_id = shirt_users_types.shirt_users_id and
shirt_users_types.shirt_orders_id = shirt_orders.shirt_orders_id and
shirt_orders.shirt_shipping_addresses_id = shirt_shipping_addresses.shirt_shipping_addresses_id and
shirt_orders.shirt_billing_addresses_id = shirt_billing_addresses.shirt_billing_addresses_id and
shirt_orders.shirt_credit_cards_id = shirt_credit_cards.shirt_credit_cards_id and
shirt_users.shirt_users_id = $shirt_users_id";
The only problem is that this query will return an empty set unless all the tables have the correct information in it. This is a problem because if a user has not ordered any items yet but I want to delete them from the database, It will not allow me. Any help on this matter would be greatly appreciated.
You have to use LEFT JOIN to link your tables (MySQL doc).
SELECT [...]
FROM shirt_users
LEFT JOIN shirt_users_types ON shirt_users.shirt_users_id = shirt_users_types.shirt_users_id
LEFT JOIN shirt_orders ON shirt_users_types.shirt_orders_id = shirt_orders.shirt_orders_id
LEFT JOIN shirt_shipping_addresses ON shirt_orders.shirt_shipping_addresses_id = shirt_shipping_addresses.shirt_shipping_addresses_id
LEFT JOIN shirt_billing_addresses ON shirt_orders.shirt_billing_addresses_id = shirt_billing_addresses.shirt_billing_addresses_id
LEFT JOIN shirt_credit_cards ON shirt_orders.shirt_credit_cards_id = shirt_credit_cards.shirt_credit_cards_id
WHERE
shirt_users.shirt_users_id = $shirt_users_id
With this you'll be able to load shirt_users even if there is no linked record in other tables.
EDIT for Delete records
If you want to delete in all your tables in only one request, you must use as syntax which is fairly the same as your first try (by replacing the SELECT statement by DELETE). ANd you will get the same problem (it will only delete records which have a linked record in each table and not null).
Method 1 :
NB : Your DB must support foreign keys (for example, MyISAM doesn't support it but InnoDB does).
You can use this method, if you always need in your app to delete all linked records.
The most beautiful way to achieve this is to add a ON DELETE CASCADE constraint on your relations :
-- Drop the old constraint ("fk_test" must be replace by your constraint name)
ALTER TABLE shirt_users_types DROP FOREIGN KEY `fk_test`;
-- Create the new with ON DELETE
ALTER TABLE shirt_users_types
ADD CONSTRAINT `fk_test`
FOREIGN KEY (`shirt_users_id` )
REFERENCES `shirt_users` (`shirt_users_id` )
ON DELETE CASCADE;
When you will delete a shirt_users entry, all linked records in shirt_users_types.
In a few words, with ON DELETE CASCADE, each time you will delete a parent element (the One side of the relation), all the children (the Many side of the relation or the table wich contains the column <related_record>_id) will be automatically deleted too.
Method 2 :
If your relation can be nullable (for example if shirt_users_types.shirt_orders_id can be null) or if your DB schema not permits to delete all needed records with ON DELETE CASCADE.
You can delete your records with several DELETE using relations to shirt_users in order to retrieve records in each linked table. (For correct syntax of DELETE with relations see MySQL docs).
In your code your unique identifier is $shirt_users_id, so you have to start by deleting in the tables which are not directly linked to shirt_users and by reaching back your relations to finish by the table shirt_users.
If you don't delete records in the correct order, you will not be able to delete all you need.
For example, with schema described in your question, if you delete records in shirt_orders before shirt_shipping_addresses, you will no longer can retrieve shipping addresses with $shirt_users_id because shirt_shipping_addresses is linked to shirt_users due to its relation with shirt_orders.
So the correct order for your current schema is (there is no need to have an order for table on the same line the list above) :
shirt_shipping_addresses / shirt_billing_addresses
shirt_orders / shirt_credit_cards / shirt_users_types
shirt_users
You can have a contraint fails error due to foreign keys. In this case you can make your delete in a TRANSACTION (it's a best practice) or disable foreign key checks for your queries.
SET foreign_key_checks = 0;

problem in mysql queries in using union

i was looking for a way to combine different mysql queries in a php file so this is my code :
$sql_query = "SELECT b.*,
u.username AS MY_Sender
FROM table_users u,
table_blogs b
WHERE b.reciever = '0'
AND
u.user_id = b.sender
UNION
SELECT b.*,
u2.username AS MY_Recipient
FROM table_users u2,
table_blogs b
WHERE b.reciever != '0'
AND
u2.user_id = b.reciever
";
this code works fine unless it cant fetch MY_Recipient
in the above code i need to fetch both sender of blog post and the receiver
is it wrong to use Union to do so ?!
I have made a guess at your table structure, and produced something similar. Right or wrong, it might at least help arrive at a suitable solution for you.
Two tables, users and blogs:
CREATE TABLE `users` (
`id` int(11) NOT NULL auto_increment,
`username` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
CREATE TABLE `blogs` (
`id` int(11) NOT NULL auto_increment,
`sender` int(11) NOT NULL,
`receiver` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
Add some users:
INSERT INTO `users` (username) VALUES
('Alice'), ('Bob'), ('Carol'), ('Eve');
Add blog entries for some users:
INSERT INTO `blogs` (sender, receiver) VALUES
(1,2), (2,1), (3,4), (4,3), (1,4), (4,1);
For each blog entry, list the sender and receiver:
SELECT
b.id,
b.sender AS sender_id,
b.receiver AS receiver_id,
us.username AS sender_name,
ur.username AS receiver_name
FROM blogs AS b
JOIN users AS us ON us.id = b.sender
JOIN users AS ur ON ur.id = b.receiver
ORDER BY b.id;
+----+-----------+-------------+-------------+---------------+
| id | sender_id | receiver_id | sender_name | receiver_name |
+----+-----------+-------------+-------------+---------------+
| 1 | 1 | 2 | Alice | Bob |
| 2 | 2 | 1 | Bob | Alice |
| 3 | 3 | 4 | Carol | Eve |
| 4 | 4 | 3 | Eve | Carol |
| 5 | 1 | 4 | Alice | Eve |
| 6 | 4 | 1 | Eve | Alice |
+----+-----------+-------------+-------------+---------------+
UPDATE 1
table_blogs should probably look like this:
CREATE TABLE IF NOT EXISTS `table_blogs` (
`bid` int(10) NOT NULL AUTO_INCREMENT,
`content` varchar(255) DEFAULT NULL,
`date` varchar(14) DEFAULT NULL,
`sender` int(10) NOT NULL,
`reciever` int(10) NOT NULL,
CONSTRAINT `fk_sender`
FOREIGN KEY (`sender` )
REFERENCES `table_users` (`id` )
ON DELETE CASCADE
ON UPDATE CASCADE,
CONSTRAINT `fk_receiver`
FOREIGN KEY (`receiver` )
REFERENCES `table_users` (`id` )
ON DELETE CASCADE
ON UPDATE CASCADE,
PRIMARY KEY (`bid`)
) ENGINE=MyISAM AUTO_INCREMENT=1 ;
The CONSTRAINT clauses will prevent inserting values for users which don't exist, and will delete entries when users are deleted from the user table.
UPDATE 2
I think this is what you want, but as KM and bobince have stated in the comments, it violates foreign key constraints, which is not really a good idea. So, assuming no foreign key constraints, here's some additional inserts and a modified query:
INSERT INTO `blogs` (sender, receiver) VALUES
(1,0), (0,1), (4,0), (0,4), (2,0), (0,2);
SELECT
b.id,
b.sender AS sender_id,
b.receiver AS receiver_id,
IFNULL(us.username, ur.username) AS sender_name,
IFNULL(ur.username, us.username) AS receiver_name
FROM blogs AS b
LEFT JOIN users AS us ON us.id = b.sender
LEFT JOIN users AS ur ON ur.id = b.receiver
ORDER BY b.id;
+----+-----------+-------------+-------------+---------------+
| id | sender_id | receiver_id | sender_name | receiver_name |
+----+-----------+-------------+-------------+---------------+
| 1 | 1 | 2 | Alice | Bob |
| 2 | 2 | 1 | Bob | Alice |
| 3 | 3 | 4 | Carol | Eve |
| 4 | 4 | 3 | Eve | Carol |
| 5 | 1 | 4 | Alice | Eve |
| 6 | 4 | 1 | Eve | Alice |
| 7 | 1 | 0 | Alice | Alice |
| 8 | 0 | 1 | Alice | Alice |
| 9 | 4 | 0 | Eve | Eve |
| 10 | 0 | 4 | Eve | Eve |
| 11 | 2 | 0 | Bob | Bob |
| 12 | 0 | 2 | Bob | Bob |
+----+-----------+-------------+-------------+---------------+
The field name should be the same
Rename My_sender and My_Recipient to "User" and the union will work.
What are you trying to do? You say there are two queries there, but it looks like the same query to me, just one of them having a different table alias.
The only purpose I can see for the UNION is to put all the rows with a zero-receiver before those without. But you can do that more simply by using a computed ORDER BY:
SELECT b.*, u.username
FROM table_blogs AS b
JOIN table_users AS u ON u.user_id=b.sender
ORDER BY b.receiver<>0
if there are no negative receiver IDs, you could change that to ORDER BY b.receiver as 0 would always come first, which would then be possible to index if you needed to;
ANSI JOIN is generally considered more readable than the old-school method of implicit joins in the WHERE conditions;
<> is preferable to !=, which is a non-standard MySQL synonym;
check the spelling of receiver.
For a union to work, the two select statements should return identical columns. This is where the query is failing.
You can do this in a single query, but if you want to use unions, the problem is that both queries need to have the same column names:
select b.*, u.username AS username, "sender" as type ...
select b.*, u2.username AS username, "recipient" as type...

Categories