PHP/SQL Not Sure How To Approach - php

I have two tables, categories and info.
categories looks like:
id | name
info looks like:
id | parent_id | name | url
parent_id is the id from categories.
I have a form where a user can add more than one url. There is a button that can be pressed to make more url fields appear. If there is more than one url, then url2, url3, etc are added to the database.
info will then look like:
id | parent_id | name | url | url2 | url3
Is this an appropriate approach?
If so, what if info is like this:
id | parent_id |name |url |url2 |url3
1 | 1 |One |http://cnn.com |
2 | 1 |Two |http://msn.com |http://aol.com|
When I view One or Two or w/e, how do I query to get their urls?
I know I can do:
$mysqli->query("SELECT i.url FROM info AS i LEFT JOIN categories AS c ON i.parent_id = c.id");
But this is dependent on me putting i.url, i.url2, i.url3 and I would have to create a separate query for each info. I want it so PHP determines how many and what i.url to select.
So it should be:
$mysqli->query("SELECT (PHP determines what i.url to put here depending on what info page I'm viewing) FROM info AS i LEFT JOIN categories AS c ON i.parent_id = c.id");

If you go that path you will have a bunch of columns with null values and encounter issues with queries, which is pretty much what you are describing.
I would create a different table (perhaps urls) that maps info with urls. For example:
url_id | info_id | url
This is the Repeated Attributes design pattern which is concisely explained here:
http://www.tomjewett.com/dbdesign/dbdesign.php?page=phone.php
Here is an example on how to query:
SELECT i.*, url
FROM info i JOIN urls u ON i.id = u.info_id

No, the right solution here is another table, perhaps called "info_urls", which models a one-to-many with info-to-urls.
This is a "normalized" design, and will allow unlimited URLs per "info", and is quite easy to query:
SELECT name, GROUP_CONCAT(info_urls.url) as urls
FROM info JOIN info_urls ON info.id = info_urls.info_id;
The columns will look like this:
id
info_id
url
This solution models the reality that you do not know how many URLs the user will enter, and your RDBMS is made to CRUD rows , not columns :-) (and is very good at joining them together for queries)

Related

Create relational tables or store serialized data in a column?

I have a users table with this structure:
user_id | name | email | mobile
And another table called products with this structure:
product_id | name | price
now I want to add wish list to my project, so that a user can add some products into his wish list (with his favorite categories. Exmaple: add a wish list category called my favorite clothes and add product_id = 55 to that category)!
Now I have 2 options:
1. Add another colum to users table called user_wish_list and then store serialized data in that column like below:
$user_wish_list = [
'user_category_1' => ['product_id_1', 'product_id_2', ....] ;
];
and store serialize($user_wish_list) in user_wish_list column.
2. create 2 tables as below:
tabe: wish_list_categories
user_id | category_id (PK) | category_title
and
tabe: wish_list_items
category_id (FK) | item_id | product_id (FK)
and finally create a relation between these tables (By SQL join)
which option is better? I personally think the second one!
one of the reasons for my choice is that I can access to data with pure SQL query
like below (I do not have to process data after fetching from database by using unserialize() ):
SELECT users.user_id, users.name, wish_list_categories.category_id,
FROM Orders
INNER JOIN Customers ON users.user_id = wish_list_categories.category_id
But what about other advantages?
Or let me ask a more general question: when we exactly use relational tables instead of use column? (consider one-to-one relationship)
Use tables. In general, don't store structured data in individual SQL values. Let the tables and foreign keys produce the structure you need.
your tables should be like:
1- users table:
user_id|user_name|Email|price
2- products table:
product_id|product_name|category_id|price
3- products_category
category_id|category_name
4- user_wish_list table:
user_id|product_id

MySQL join query duplicates users in output

I have the following tables
ea_users
id
first_name
last_name
email
password
id_roles
ea_user_cfields
id
c_id = custom field ID
u_id = user ID
data
ea_customfields
id
name = name of custom field
description
I want to get all users which have a certain role, but I also want to retrieve all the custom fields per user. This is for the backend of my software where all the ea_users and custom fields should be shown.
I tried the following, but for each custom field, it duplicates the same user
$this->db->join('(SELECT GROUP_CONCAT(data) AS custom_data, id AS dataid, u_id, c_id
FROM ea_user_cfields userc
GROUP BY id) AS tt', 'tt.u_id = ea.id','left');
$this->db->join('(SELECT GROUP_CONCAT(name) AS custom_name, id AS customid
FROM ea_customfields AS cf
GROUP BY id) AS te', 'tt.c_id = te.customid','left');
$this->db->where('id_roles', $customers_role_id);
return $this->db->get('ea_users ea')->result_array();
the problem that u did not understand properly how join works.
its ok, that u have duplicates in select when u have relation one to many.
in few words your case: engine tries to fetch data from table "A" (ea_users) then JOIN according to the conditions another table "B" (ea_customfields). If u have one to many relation between tables (it means that one record from table "A" (lets say that we have in this table A1 record) can contain few related rows in table "B", lets call them as B1.1, B1.2 and B1.3 and B1.4), in this case it will join this records and put join result in memory. So in memory u would see something like
| FromTable A | FromTableB |
| A1 | B1.1 |
| A1 | B1.2 |
| A1 | B1.3 |
| A1 | B1.4 |
if u have 10 records in table "B", which related to the table "A" it would put 10 times in memory copy of data from table "A" during fetching. And then will render it to u.
depending on join type rows, with missing related records, can be skipped at all (INNER JOIN), or can be filled up with NULLs (LEFT JOIN or RIGHT JOIN), etc.
When u think about JOINs, try to imagine yourself, when u try to join on the paper few big tables. U would always need to mark somehow which data come from which table in order to be able to operate with it later, so its quite logically to write row "A1" from table "A" as many times as u need to fill up empty spaces when u find appropriate record in table "B". Otherwise u would have on your paper something like:
| FromTable A | FromTableB |
| A1 | B1.1 |
| | B1.2 |
| | B1.3 |
| | B1.4 |
Yes, its looks ok even when column "FromTable A" contains empty data, when u have 5-10 records and u can easily operate with it (for example u can sort it in your head - u just need to imagine what should be instead of empty space, but for it, u need to remember all the time order how did u wrote the data on the paper). But lets assume that u have 100-1000 records. if u still can sort it easily, lets make things more complicated and tell, that values in table "A" can be empty, etc, etc.. Thats why for mysql engine simpler to repeat many times data from table..
Basically, I always stick to examples when u try to imagine how would u join huge tables on paper or will try to select something from this tables and then make sorting there or something, how would u look through the tables, etc.
GROUP_CONCAT, grouping
Then, next mistake, u did not understand how GROUP_CONCAT works:
The thing is that mysqlEngine fetch on the first step structure into memory using all where conditions, evaluating subqueries + appends all joins. When structure is loaded, it tried to perform GROUPing. It means that it will select from temporary table all rows related to the "A1". Then will try to apply aggregation function to selected data. GROUP_CONCAT function means that we want to apply concatenation on selected group, thus we would see something like "B1.1, B1.2, B1.3, B1.4". Its in few words, but I hope it will help a little to understand it.
I googled table structure so u can write some queries there.
http://www.mysqltutorial.org/tryit/query/mysql-left-join/#1
and here is example how GROUP_CONCAT works, try to execute there query:
SELECT
c.customerNumber, c.customerName, GROUP_CONCAT(orderNumber) AS allOrders
FROM customers c
LEFT JOIN orders o ON (c.customerNumber = o.customerNumber)
GROUP BY 1,2
;
can compare with results with previous one.
power of GROUP in aggregation functions which u can use with it. For example, u can use "COUNT()", "MAX()", "GROUP_CONCAT()" or many many others.
or example of fetching of count (try to execute it):
SELECT c.customerName, count(*) AS ordersCount
FROM customers AS c
LEFT JOIN orders AS o ON (c.customerNumber = o.customerNumber)
GROUP BY 1
;
so my opinion:
simpler and better to solve this issue on client side or on backend, after fetching. because in term of mysql engine response with duplication in column is absolutely correct. BUT of course, u can also solve it using grouping with concatenations for example. but I have a feeling that for your task its overcomplicating of logic
PS.
"GROUP BY 1" - means that I want to group using column 1, so after selecting data into memory mySql will try to group all data using first column, better not to use this format of writing on prod. Its the same as "GROUP BY c.customerNumber".
PPS. Also I read comments like "use DISTINCT", etc.
To use DISTINCT or order functions, u need to understand how does it work, because of incorrect usage it can remove some data from your selection, (same as GROUP or INNER JOINS, etc). On the first look, you code might work fine, but it can cause bugs in logic, which is the most complicated to find out later.
Moreover DISTINCT will not help u, when u have one-to-many relation(in your particular case). U can try to execute queries:
SELECT
c.customerName, orderNumber AS nr
FROM customers c
INNER JOIN orders o ON (c.customerNumber = o.customerNumber)
WHERE c.customerName='Alpha Cognac'
;
SELECT
DISTINCT(c.customerName), orderNumber AS nr
FROM customers c
INNER JOIN orders o ON (c.customerNumber = o.customerNumber)
WHERE c.customerName='Alpha Cognac'
;
the result should be the same. Duplication in customer name column and orders numbers.
and example how to loose data with incorrect query ;):
SELECT
c.customerName, orderNumber AS nr
FROM customers c
INNER JOIN orders o ON (c.customerNumber = o.customerNumber)
WHERE c.customerName='Alpha Cognac'
GROUP BY 1
;

avoid MySQL query from eating unnecessary bandwidth

I am concerned about optimizing my queries as far as the amount of data that is sent back from the database after doing a query.
Let's say that I have 2 tables. One called "artists" and another called "albums".
Let's say "artists" table columns are: id and name
while "albums" table columns are: id, artist_id, title
Let's say that I want a page to diplay the artist's name as the heading. And then below that, I want to display a list of the artist's albums.
I can get that done easily by doing something like:
SELECT artists.name AS artist_name, albums.title AS album_title
LEFT JOIN albums
ON albums.artist_id = artists.id
WHERE artists.id = 3;
This would give me a result that could look something like:
artist_name | album_title
|
Justin Bieber | My First Crappy Album
Justin Bieber | Another Crappy Album
Justin Bieber | Yet Another Crappy Album
The problem with this result is that it gives me back the artist name multiple times, when I only really need it once. I am concerned that this way of doing things is not very efficient. Especially if the artist already has plenty of albums. I could be wrong about this, and if I am, someone please correct me.
In any case, would there be a better way of doing this? One where I don't have to retrieve the artist name multiple times?
You can use the aggregate functions.
SELECT ar.name AS artist_name, GROUP_CONCAT(al.title) AS album_titles
FROM artists ar, albums al
WHERE ar.id = al.artist_id
AND ar.id = 3
GROUP BY artist_name;
This should give you something like:
artist_name | album_titles
Justin Bieber | Album1, Album2, Album3
I haven't used this command in a while, but you can find more documentation on it here: http://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html#function_group-concat
Also, I prefer to list my tables in the FROM clause and use the WHERE clause to join them, but it's the same as your JOIN above... just a different syntax.
Then do two seperate requests.
First request the artist name
SELECT name
FROM artists
WHERE id = 3
Then the album titles..
SELECT title
FROM albums
WHERE artist_id = 3
I don't know about any bandwith logging inside php, but you can calculate witch way is the fastest in executing using microtime()

How to select a value from a column which is based on other row and column value?

Well, best explained with a example:
MYSQL TABLE NAME: CLIENTS
ID NAME CODE
1 Mark 0
2 Joe 1
What I want:
HTML TABLE:
MATCH NAME
Mark Joe
Did you get it? Like, I need to take the NAME from the person who has a ID equal to the CODE of other.
I think that you won't need my code, since I just want to know the function that allows me to echo two distinct "NAMES". Like, if I call two echo $name; how the code knows who is who?
Ps.: I'm not a back-end developer, take it easy =)
PsĀ².: It is a loop (what I know to do hehehehe), it has to repeat that "search" and list a lot of "MATCHES", you know?
we can find reord in the same table by using self join...
SELECT e.NAME as parent, m.NAME as child FROM CLIENTS e, CLIENTS m WHERE e.ID = m.CODE
Hope this will be your solution
Using Mysql JOIN
SELECT b.name, a.name as match_name
FROM clients a
JOIN clients b
ON a.id = b.code;
Output
+------+------------+
| name | match_name |
+------+------------+
| Joe | Mark |
+------+------------+

SQL - create relation to echo data from two tables

I made some researchs but couldnt find yet the exact concept needed to achieve this:
::: EDIT ::: my initial request wasnt clear so here I try again
I have two tables,
table_01 with four columns: id / name / address / id_cat
AND
table_02 with two columns: id_cat / category
id_cat in table_01 and table_02 is just an INT(10), while category is a VARCHAR supposed to contain the exact name of the category (which is quite long). What I am trying to do is, when a query is made on table_01, the echo shows rows of table_01 as result, but instead of showing id_cat as a number, it shows the text from table_02 category.
The result shows as I am expecting it, but, if for example in table_02, I have a rowid_cat = 1 , category = AAAAA, I would like to echo the id_cat from table_01 as the category from table_02, so still for example, 002 | standon | 125 market street | AAAAA instead of what I get now, which is 002 | standon | 125 market street | 1.
I didnt create relation between both tables yet.
If I understand what you are after, your query for the data would just need to be something like:
select t1.id as id, t2.category as category from tab_01 as t1
left join tab_02 as t2 on t1.id_cat = t2.id_cat where t1.id = '5';
The 5 is obviously the id you are looking for in tab_01.
I am not fully clear about your question. But I think this will help you.
Right now you have created one-to-many relationship between this two tables. This means one category (tab_02) can have multiple tab_01 data.
You can try this query:
SELECT t1.id_cat AS id_cat_number, t2.*
FROM tab_02 t2
INNER JOIN tab_01 t1 ON t1.id_cat = t2.id_cat
WHERE t2.category = XXXXXXXX

Categories