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...
Related
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 |
+----+----------------+-----------+-----------------+------+---------+------------------------------+
I am currently working on a user system, and would like to setup multiple usergroups/ranks per member. I see other posts on here explaining it by using foreign keys, denormalized tables, etc... but they're all from 2010-2012, so wasn't sure if there were easier/better standard ways of doing it.
CREATE TABLE IF NOT EXISTS `users` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL,
`password` varchar(80) NOT NULL,
`email` varchar(80) NOT NULL,
`character_name` varchar(50) NOT NULL,
`verify` int(5) NOT NULL,
`rank` varchar(50) NOT NULL,
`position` int(10) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE INDEX (`email`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=3;
CREATE TABLE IF NOT EXISTS `rank` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`rank` varchar(50) NOT NULL,
`position` int(10) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=26 ;
The above are my two tables. Here are the two table structures with three examples in each:
users
id | username | password | email | character_name | verify | rank | position
1 | userA | passA | A#A | characterA | yes | mem | 1
2 | userB | passB | B#B | characterB | yes | mod | 1
3 | userC | passC | C#C | characterC | yes | adm | 3
rank
id | rank | position
1 | mem | 1
2 | mod | 1
3 | adm | 3
Users should be able to be in multiple ranks.
userA should only be a mem.
userB should be a mem and mod.
userC should be a mem, mod, and adm.
If I were to join both tables:
SELECT * FROM rank INNER JOIN users ON rank.position = users.position;
Would that cause userA to be mem and mod as well, since the position for both is 1?
Would it make more sense to remove rank and position from users, position from rank, and join based on user id?
For example (question 2):
users
id | username | password | email | character_name | verify
1 | userA | passA | A#A | characterA | yes
2 | userB | passB | B#B | characterB | yes
3 | userC | passC | C#C | characterC | yes
rank
id | rank
1 | mem
2 | mem
2 | mod
3 | mem
3 | mod
3 | adm
SELECT * FROM rank INNER JOIN users ON rank.id = users.id;
Once selected, I want certain ranks to be able to do certain things.
$sql = SELECT * FROM rank INNER JOIN users ON rank.id = users.id;
$ranks = $conn->query($sql);
$ranks->execute();
foreach ($ranks as $row) {
if($row['rank'] == "mem" {
Do something.
}
if($row['rank'] == "mod" {
Do something else.
}
if($row['rank'] == "adm" {
Do another something else.
}
}
Is the above correct?
Edited, because my first posting apparently wasn't clear enough.
Can anyone help?
i've three tables
comments table
---------------------------------------------------------------------------
id | commented_by | comment | commented_by
---------------------------------------------------------------------------
1 | asdf156df5s4fd | student comment | student
2 | klsdfjklsdfno | student comment | student
3 | jafkadfjlkdnvwa | teacher comment | teacher
4 | adsjflandsfinndfs | student comment | student
5 | dsf5614dsf8wef4 | teacher comment | teacher
---------------------------------------------------------------------------
teacher table
id | teacher_id | teacher_name | teaches
---------------------------------------------------------------------------
1 | jafkadfjlkdnvwa | abc cde | maths
2 | dsf5614dsf8wef4 | egc kjl | science
.
.
.
---------------------------------------------------------------------------
student table
id | student_id | student_name |
---------------------------------------------------------------------------
1 | asdf156df5s4fd | student_1 |
2 | klsdfjklsdfno | student_2 |
3 | adsjflandsfinndfs | student_3 |
.
.
---------------------------------------------------------------------------
now, what we wish is mysql join which could return something like this
id | comment_id | comment_by | name | teaches
---------------------------------------------------------------------------
1 | asdf156df5s4fd | student | student_1 | null
2 | klsdfjklsdfno | student | student_2 | null
3 | jafkadfjlkdnvwa | teacher | abc cde | maths
4 | adsjflandsfinndfs | student | student_3 | null
5 | dsf5614dsf8wef4 | teacher | egc kjl | science
---------------------------------------------------------------------------
unable to find the exact query with which we can achieve this with codeigniter active records
You need to join student and teacher table:
select id, comment_id as comment_by , commented_by,
( case when commented_by = 'teacher' THEN teacher_name else student_name ) AS name, teaches
from comments c left join teacher t ON t.teacher_id = c.commented_by left
join student s ON s.student_id = c.commented_by
It's real dirty table scheme but this is the query you want:
SELECT
c.id,
commented_by as comment_id,
commented_by,
IF(commented_by = 'student', student_name, teacher_name) as name,
teaches
FROM comments c
LEFT JOIN teacher t
ON t.teacher_id = c.commented_by
AND c.commented_by = 'teacher'
LEFT JOIN student s
ON s.student_id = c.commented_by
AND c.commented_by = 'student'
A better table scheme:
CREATE TABLE comments (
comment_id INT NOT NULL AUTO_INCREMENT,
student_id INT,
teacher_id INT,
PRIMARY KEY (comment_id)
);
CREATE TABLE teachers (
teacher_id INT NOT NULL AUTO_INCREMENT,
teacher_name VARCHAR(200) NOT NULL,
teacher_subject VARCHAR(50),
PRIMARY KEY (teacher_id)
);
CREATE TABLE students (
student_id INT NOT NULL AUTO_INCREMENT,
student_name VARCHAR(200) NOT NULL,
PRIMARY KEY (student_id)
);
SELECT
comments.*,
IF(student_id IS NOT NULL, student_name, teacher_name) as name,
teacher_subject
FROM comments
LEFT JOIN teachers USING(teacher_id)
LEFT JOIN students USING(student_id)
I'm trying to create a book-catalogue. I have 3 basic tables - books, authors, books_authors;
books
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| book_id | int(11) | NO | PRI | NULL | auto_increment |
| book_title | varchar(250) | NO | | NULL | |
+------------+--------------+------+-----+---------+----------------+
authors
+-------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+--------------+------+-----+---------+----------------+
| author_id | int(11) | NO | PRI | NULL | auto_increment |
| author_name | varchar(250) | NO | | NULL | |
+-------------+--------------+------+-----+---------+----------------+
books_authors
+-----------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------+---------+------+-----+---------+-------+
| book_id | int(11) | NO | MUL | NULL | |
| author_id | int(11) | NO | MUL | NULL | |
+-----------+---------+------+-----+---------+-------+
I have a query that takes the book name and all authors for each book and displays the result:
$booksAndAuthors = mysqli_query($connection, 'SELECT * FROM books LEFT JOIN books_authors ON books.book_id=books_authors.book_id LEFT JOIN authors ON authors.author_id=books_authors.author_id');
It returns:
Book Name -> Author 1, Author 2
Book Name 2 -> Author 3, Author 2
And so on.
And I have another query that it's:
$booksAndAuthors = mysqli_query($connection, 'SELECT * FROM books_authors as ba
INNER JOIN books as b ON ba.book_id=b.book_id
INNER JOIN books_authors as booaut ON booaut.book_id=ba.book_id
INNER JOIN authors as a ON booaut.author_id=a.author_id
WHERE ba.author_id=' . $author_id);
When I click over an author (authors are links), the query returns all books of an author the opposite; The queries all work;
My Question is:
Could someone explain to me why I'm comparing a table with itself. Just explain for dummie like myself. I want to understand the action that is done by this query, with words or something else.
*If my question isn't properly asked! Edit me!
*Regards!
A book can have more than one author. The point of the self-join is to find the other authors for the book.
FROM books_authors as ba
...
INNER JOIN books_authors as booaut ON booaut.book_id=ba.book_id
...
WHERE ba.author_id=42
The join picks up any author who co-authored a book with author 42.
Another way to write the query:
FROM books_authors as ba
...
WHERE EXISTS
(
SELECT *
FROM books_authors ba2
WHERE ba2.book_id = ba.book_id
and ba2.author_id = 42
)
This says, select all rows where a matching book_authors entry exists for author 42.
It seems a self join ( joining the table with itself ) is unnecessary here since you are picking the same rows.
Usually self joins are performed to join two different rows in a table. For example, if you have a table with monthly account balances for example
acount_id |as_of_date | balance_amount
-----------|---------------------------
12213 |2014-01-01 | 10000
12213 |2014-02-01 | 20000
12213 |2014-03-01 | 25000
Let's say the table name is monthly_account_balances
Now you want to compute the difference between monthly balances
For instance, between February and January the difference is 20000 - 10000 = 10000
And between March and February the difference is 25000 - 20000 = 5000
And the output you need is
acount_id |as_of_date | balance_amount|difference
-----------|-----------|---------------|-------------
12213 |2014-01-01 | 10000 | null
12213 |2014-02-01 | 20000 | 10000
12213 |2014-03-01 | 25000 | 5000
Here you do a self join as follows:
select a.*, b.balance_amount - a.balance_amount as difference
from monthly_account_balances a
inner join monthly_account_balances b on a.account_id = b.account_id
and a.as_of_date + interval '1 month' = b.as_of_date
Notice the date condition. It's comparing two different records with same id but different as_of_date. Self join is useful in such situations.
However in your case you are just joining on id and I see no point in doing that unless I am missing something
I don't know how to write the SQL syntax of getting the last record (according to recent post and is not replied to).
My table
+-------------------+-----------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------------+-----------------------+------+-----+---------+----------------+
| notification_id | mediumint(8) unsigned | NO | PRI | NULL | auto_increment |
| user_id | mediumint(8) unsigned | NO | | NULL | |
| notification_msg | text | NO | | NULL | |
| notification_date | int(11) unsigned | NO | | NULL | |
| private_message | tinyint(1) unsigned | NO | | 0 | |
| has_replied | tinyint(1) unsigned | NO | | 0 | |
| reply_id | mediumint(8) unsigned | NO | | 0 | |
+-------------------+-----------------------+------+-----+---------+----------------+
Basically for each threaded notification, it should get the last record of each notification record and check if has_replied is 0, if it is 0 then it should return it so PHP can read whether there is a notification that hasn't been replied to. So like, it should return like this (pseudo):
+--------------+-----+-----+
| username | 1 | 4 |
| username2 | 0 | 2 |
+--------------+-----+-----+
Where the second column represents if the last post has been replied to or not.
My current SQL syntax (works but does not get the last record and if it's replied to):
SELECT n.*,
m.user_id,
m.username
FROM notifications n
INNER JOIN members m ON n.user_id = m.user_id
WHERE private_message = 1
AND reply_id = 0
ORDER BY has_replied ASC,
notification_date DESC
Select m.user_id, m.username
, N...
From members As M
Join (
Select user_id, Max( notification_id ) As notification_id
From notifications
Group By user_id
) As UserLastNotification
On UserLastNotification.user_id = m.user_id
Join notifications As N
On N.notification_id = UserLastNotification.notification_id
Where N.private_message = 1
And N.reply_id = 0
Order By N.has_replied, N.notification_date Desc
Note that this will filter on each user's last notification being a private message and having a reply_id being zero.
A simple
LIMIT 1
at the end of the query should be sufficient to only return the last post.