Single query SELECT propagate to childs - php

i have this table structure:
id
name
email
child_id <- pointing to id from "child" table
how SELECT this table data and propagate select to all childs with a SINGLE QUERY?

If you assume you're working with a child table with its own name and id field, you could write...
select c.name as child_name, p.name as parent_name
from child c
join parent p
on c.id = p.child_id
I made a SQLFiddle with the following SQL:
CREATE TABLE "parent" (
"id" INTEGER PRIMARY KEY,
"name" TEXT,
"email" TEXT,
"child_id" INTEGER
);
CREATE TABLE "child" (
"id" INTEGER PRIMARY KEY,
"name" TEXT
);
INSERT INTO parent
(id, name, email, child_id )
VALUES
(1, "Jim", "pineapple#fruit.com", 1 ),
(2, "Jane", "gizmo#gadget.com", 1 ),
(3, "Grover", "monty#python.com", 2 );
INSERT INTO child
(id, name)
VALUES
(1, "Ben" ),
(2, "Samantha" ),
(3, "Carl" );
My select query then gave me:
However, this data structure limits you to only on child per parent though I showed 2 parents for one of the kids. If you want multiple of either without having to duplicate the parent entries, you'd want to have a third table for the parent child relationship. That Table would have a parent_id, a child_id, and there are different schools of thought on whether it having it's own id would be required (it makes selecting specific rows in the relationship table easy and becomes more important if the relationship gains additional fields itself). Your query would need to join all the tables to the relationship table and if you did this, that part might look something like this:
from relationship r
join parent p
on p.relation_id = r.parent_id
join child c
on c.relation_id = r.child_id

Related

How to query for many to many relationship between products and filters in MySQL?

I have three tables viz. tb_filters, tb_products, and tb_products_to_filters. The structure of these tables along with some dummy data is given by:
tb_filters:
CREATE TABLE IF NOT EXISTS `tb_filters`
(
`filter_id` INT (11) AUTO_INCREMENT PRIMARY KEY,
`filter_name` VARCHAR (255)
);
INSERT INTO `tb_filters`
(`filter_name`)
VALUES ('USB'),
('High Speed'),
('Wireless'),
('Ethernet');
tb_products:
CREATE TABLE IF NOT EXISTS `tb_products`
(
`product_id` INT (11) AUTO_INCREMENT PRIMARY KEY,
`product_name` VARCHAR (255)
);
INSERT INTO `tb_products`
(`product_name`)
VALUES ('Ohm precision shunt resistor'),
('Orchestrator Libraries'),
('5cm scanner connection'),
('Channel isolated digital'),
('Network Interface Module');
tb_products_to_filters:
CREATE TABLE IF NOT EXISTS `tb_products_to_filters`
(
`id` INT (11) AUTO_INCREMENT PRIMARY KEY,
`product_id` INT (11),
`filter_id` INT (11)
);
INSERT INTO `tb_products_to_filters`
(`product_id`, `filter_id`)
VALUES (1, 1),
(2, 2),
(3, 3),
(4, 3),
(1, 3);
By looking into above "tb_products_to_filters" table, my required queries are:
When filter id = 1 and 3 are selected via checkbox on the page, all those products which belong to filter id 1 as well as filter id 3 must be fetched from the database. In this case, the product with id 1 should come.
Second, when only one filter (say id = 3) is checked, then all those products which fall under this id should be fetched. In this condition, the products id 1, 3 and 4 will come.
If filter id 2 is selected, then only one product with id = 2 will come.
If combination of filter (2 and 3) is selected, then no product will come because there is no product which belongs to both of them.
What is the way of writing queries to obtain above goal?
Please note that I want to include columns: product_id, product_name, filter_id and filter_name to display data in table result set.
EDIT:
The output should match below when filter ids 1 and 3 were checked:
EDIT 2:
I'm trying below query to fetch results when filter 1 and 3 were checked:
SELECT `p`.`product_id`, `p`.`product_name`,
GROUP_CONCAT(DISTINCT `f`.`filter_id` ORDER BY `f`.`filter_id` SEPARATOR ', ') AS filter_id, GROUP_CONCAT(DISTINCT `f`.`filter_name` ORDER BY `f`.`filter_name` SEPARATOR ', ') AS filter_name
FROM `tb_products` AS `p` INNER JOIN `tb_products_to_filters` AS `ptf`
ON `p`.`product_id` = `ptf`.`product_id` INNER JOIN `tb_filters` AS `f`
ON `ptf`.`filter_id` = `f`.`filter_id` GROUP BY `p`.`product_id`
HAVING GROUP_CONCAT(DISTINCT `ptf`.`filter_id` SEPARATOR ', ') = ('1,3')
ORDER BY `p`.`product_id`
But unfortunately, it returns an empty set. Why?
You can use the HAVING clause with GROUP_CONCAT :
SELECT t.product_id,tp.product_name,
GROUP_CONCAT(t.filter_id) as filter_id,
GROUP_CONCAT(tb.filter_name) as filter_name
FROM tb_products_to_filters t
INNER JOIN tb_filters tb ON(t.filter_id = tb.filter_id)
INNER JOIN tb_products tp ON(t.product_id = tp.product_id)
WHERE t.filter_id IN(1,3)
GROUP BY t.product_id
HAVING COUNT(distinct t.filter_id) = 2
You can adjust this any way you want. Note that the number of arguments placed inside the IN() should be the same as the COUNT(..) = X
EDIT:
A DISTINCT keyword is required in GROUP_CONCAT while fetching those columns otherwise all the filters would come in the list. I tried it by doing
SELECT t.product_id,tp.product_name,
GROUP_CONCAT(DISTINCT t.filter_id ORDER BY `t`.`filter_id` SEPARATOR ', ') as filter_id,
GROUP_CONCAT(DISTINCT tb.filter_name ORDER BY tb.filter_name SEPARATOR ', ') as filter_name
FROM tb_products_to_filters t
INNER JOIN tb_filters tb ON(t.filter_id = tb.filter_id)
INNER JOIN tb_products tp ON(t.product_id = tp.product_id)
WHERE t.filter_id IN(1,3)
GROUP BY t.product_id
HAVING COUNT(distinct t.filter_id) = 2
But still all the filter names (Ethernet, High Speed, USB, Wireless) are coming in the list. How to list only those filter names whose corresponding filter id (1, 3) are in the string?

Insert data in 3rd table with the values inserted in 2 other table

I have 3 table in postgres database. Created with this code:
CREATE TABLE AUTHOR(
ID SERIAL PRIMARY KEY,
NAME TEXT
);
CREATE TABLE BOOK(
ID SERIAL PRIMARY KEY,
NAME TEXT
);
CREATE TABLE BOOK_AUTHOR(
BOOK_ID INTEGER REFERENCES BOOK(ID),
AUTHOR_ID INTEGER REFERENCES AUTHOR(ID)
);
A book can have multiple author.
I want to insert multiple author in AUTHOR table.
A book in BOOK table.
And pair in BOOK_AUTHOR table.
For example: If BOOK X is written by Mr. A and Mr. B
I want the table content be like this
AUTHOR
ID-NAME
1, Mr. A
2, Mr. B
BOOK
ID-NAME
1, X
BOOK_AUTHOR
BOOK_ID-AUTHOR_ID
1,1
1,2
I am using postgres-php.
I know I can insert data in author table. Insert data in book table. Make query over them to get the ids.
Then insert in book_author table.
But is there any way to insert those data more efficiently?
What is the possible best way?
PostgreSQL has a very handy 'RETURNING' function you can use here like this:
WITH authors AS (
INSERT INTO
author (name)
VALUES
('Mr. A'), ('Mr. B')
RETURNING
id
), books AS (
INSERT INTO
book (name)
VALUES
('X')
RETURNING
id
)
INSERT INTO
book_author
SELECT
b.id
, a.id
FROM
books b
, authors a;
Just make a Cartesian product of the output and use it as input for the third insert.

Multiples SQL Joins

I have this problem and I haven't been able to figure out how can I make this work.
I have a table called "parents" and a table called "students"
Parents table has
id, name, lastname and type. "Type" cantains something like "mother" or "father".
Students table has
id, name, lastname, motherid(parents table), fatherid(parents table).
How can I make a query with JOINS that returns the Mother's name and the Father's name? And knowing that the parents can have multiple children, and obviously a student can have two parents, Mother and Father...
I hope I explained it well, thank you beforehand
This is the simple way to do what you're asking:
(
SELECT KIDS1.id as student_id
KIDS1.name as student_firstname
KIDS1.lastname as student_lastname
MOMS.id as parent_id
MOMS.name as parent_firstname
MOMS.lastname as parent_lastname
MOMS.type as parent_type
FROM students KIDS1
JOIN parents MOMS ON KIDS1.motherid = MOMS.id
)
UNION
(
SELECT KIDS2.id as student_id
KIDS2.name as student_firstname
KIDS2.lastname as student_lastname
DADS.id as parent_id
DADS.name as parent_firstname
DADS.lastname as parent_lastname
DADS.type as parent_type
FROM students KIDS2
JOIN parents DADS ON KIDS2.fatherid = DADS.id
)
I've updated my query now so that you can get a list of the parents and what kind of parent it is (mother or father). This query will not list students that don't have a parent listed in your system. You will have to modify or write a different query to determine which students do not have a parent listed.
You can do this with two Inner joins (not sure if this is the best way)
Your two joins would look something like:
Select students.name, parents.name
FROM students
INNER JOIN Parents
ON motherid = Parents.id
INNER JOIN Parents
ON fatherid = Parents.id

How to associate Article with multiple categories

New to PHP/MySQL and building a CMS. I need to know how to associated an Article with multiple Categories eg "My Article 1" has to be in 3 categories and "My Article 2" has to be in only 2 etc.
It's basically just the table set up to begin with. Here's what I have:
Table = articles
Fields = id, publicationDate, title, summary, content
Table = categories
Fields = id, categoryTitle
I know a little about joins and relationships etc but I want to get this right from the start.
You need a many-to-many table that will link article ids to category ids
CREATE TABLE articles_categories (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
articles_id INT,
category_id INT
) TYPE=myisam;
So for each category that you associate with an article you will need to insert a row into this table.
Perhaps something like:
$currentArticle = array('id' => 99, 'name' => 'Test Article');
$currentCategory = array('id' => 1, 'name' => 'First Category');
mysql_query(sprintf('INSERT INTO articles_categories (articles_id, category_id) VALUES (%d, %d)', $currentArticle['id'], $currentCategory['id']));
You need a third table, called articles_to_categories or whatever, with two fields:
article_id
category_id
Add an entry for each article and category association. Set the PK to be both columns.

How to model a many to many relationship?

With only a bit of previous experience with databases and no formal education with them, I'm a bit stuck as to how to model this (and retrieve the data I require from it in PHP). This is what I'm trying to model:
For each item on my site, it is allowed to have multiple tags such as file, upload, php, recursive etc. However the tags are reusable, I can have two different items and they each could have the php tag.
I've been trying to read up on how to do this and whether it is lack of experience with it or something else I don't know, but I can't seem to grasp the concept. Apparently you need a middle table which links the two together?
Also once I have this relationship and the tables defined, how would I do things such as:
- Retrieve all items with a certain tag?
- Retrieve all tags that one item has?
Thanks for your help, also if anyone could list any further reading on this to strengthen my understanding of the concept that would be great.
The db part is easy. This is just a sample so you can see how db can look like, not any particular SQL engine queries.
CREATE TABLE posts (
id INT PRIMARY KEY,
subject VARCHAR(100),
body TEXT
)
CREATE TABLE tags (
id INT PRIMARY KEY,
name VARCHAR(50)
)
CREATE TABLE post_tags (
post_id INT,
tag_id INT,
FOREIGN KEY (post_id) REFERENCES posts (id),
FOREIGN KEY (tag_id) REFERENCES posts (id)
)
To get items with yourTag tag you will just run query like this
SELECT P.*
FROM posts P
LEFT JOIN post_tags PT ON (PT.post_id = P.id)
LEFT JOIN tags T ON (T.id = PT.tag_id)
WHERE T.name = 'yourTag';
To get tags associated with post with id of 123 you run this query:
SELECT T.*
FROM tags T
LEFT JOIN post_tags PT ON (T.id = PT.tag_id)
LEFT JOIN posts P ON (PT.post_id = P.id)
WHERE P.id = 123;
For the PHP part you could use a framework. Many (if not all) frameworks can easily model such relationships. For example in CakePHP this done like that:
class Post extends AppModel {
$useTable = 'posts';
$hasAndBelongsToMany = array(
'Tag' => array(
'className' => 'Tag'
'joinTable' => 'post_tags'
'foreignKey' => 'post_id'
'associationForeignKey' => 'tag_id'
)
);
}
class Tag extends AppModel {
$useTable = 'tags';
$hasAndBelongsToMany = array(
'Post' => array(
'className' => 'Post'
'joinTable' => 'post_tags'
'foreignKey' => 'tag_id'
'associationForeignKey' => 'post_id'
)
);
}
You should use an intermediate table, to relate the two entities:
-------- 1:n ------------ ---------
| ITEM |-¦---------<| ITEM_TAG | n:1 | TAG |
| Id | | ItemId |>-------¦-| Id |
| Name | | TagId | | Name |
¯¯¯¯¯¯¯¯ ¯¯¯¯¯¯¯¯¯¯¯¯ ¯¯¯¯¯¯¯¯¯
Then for querying the data, you should join your tables in a select statement:
All the items in the tag "FooTag"
SELECT item.* FROM item
JOIN item_tag on item.id = item_tag.itemId
JOIN tag on item_tag.tagId = tag.id
WHERE tag.Name = 'FooTag'
All the tags for the item with name "FooItem"
SELECT tag.* FROM tag
JOIN item_tag on tag.id = item_tag.tagId
JOIN item on item_tag.itemId = item.id
WHERE item.Name = 'FooItem'
You're right, many-to-many relationships are implemented using additional table, for instance:
Blog_entry(entry_id, entry_body)
Tag(tag_id, tag_name)
Entry_tag(entry_id, tag_id)
Any operations are being done using multiple joins. For instance, if you want to select all entries with tag 'foo', using tables from my example, you have to execute:
select *
from
blog_entry, tag, entry_tag
where
tag.tag_name = 'foo' and
entry_tag.tag_id = tag.tag_id and
entry_tag.entry_id = blog_entry.entry_id
(update) To retrieve all tags that certain entry (here with ID 123) has:
select tag_name
from
blog_entry, tag, entry_tag
where
Blog_entry.entry_id = 123
entry_tag.tag_id = tag.tag_id and
entry_tag.entry_id = blog_entry.entry_id
Yeah, many-to-many relationship needs additional, third table, called association table.
Database part is not that hard, it's harder to use it in code with all those left joins and it can get pretty messy :)
My advice is to use ORM framework, like Doctrine or Propel (though I prefer Doctrine), which handle even some complex queries for you.

Categories