I please need some help:
I have this database, which has this fields with their respect values:
agency_id | hostess_id
3 | 12-4-6
5 | 19-4-7
1 | 1
In hostess_id are stored all hostesses ids that are associated with that agency_id but separated with a "-"
Well, i login as a hostess, and i have the id=4
I need to retrieve all the agency_id which contain the id=4 , i can't do this with like operator.. i tried to do it by saving the hostess_id row to an array, then implode it, but i can't resolve it like this.
Please, please any idea?
You should change your database design. What you are describing is a typical N:N relation
Agencies:
agency_id | name
3 | Miami
5 | Annapolis
1 | New York
Hosteses
Hostes_id | name
4 | Helen
12 | May
19 | June
AgencyHostes
Hostes_id | agency_id
4 | 1
4 | 3
4 | 5
12 | 1
12 | 3
19 | 1
First, let me say that I absolutely agree with #JvdBerg on that this is terrible database design that needs to be normalized.
Let's think for a minute though, that you have no way of changing the database layout and that you must solve this with SQL, an inefficient but working solution would be
select agency_id from tablename where
hostess_id LIKE '4-%' OR
hostess_id LIKE '%-4-%' OR
hostess_id LIKE '%-4'
if you were searching for all agencies with hostess id 4. I build this on sqlfiddle to illustrate more thoroughly http://sqlfiddle.com/#!2/09a52/1
Mind though, that this SQL statement is hard to optimize since an index structure for substring matching is rarely employed. For very short id lists it will work okay though. If you have ANY chance at changing the table structure, normalize your schema like #JvdBerg suggested and look up database design and normal forms on google.
Related
This question already has answers here:
MySQL query finding values in a comma separated string
(11 answers)
Closed 6 years ago.
I have a table named 'offers' that has a column in_stores. It contains the ids of the stores that this offer is available in. Graphically this table looks like this:
id | title | in_stores
1 | Shoes | 1002,1003
2 | Gloves | 1020,1011
3 | Shades | 1002,1009
4 | Hats | 1010,1002
5 | Shoes | 1220
6 | Shirts | 1010
7 | Hats | 1002
Each value in in_stores is saved with the implode() function through PHP.
My question:
How to select in a single mysqli query all the offers that are available in a store with id 1002. In this example the query should return offers with id 1,3,4,7. I guess I should use something like explode() first to get the results in an array and than in_array() to search for the specific id in it but all those functions are unavailable within SQL.
This is a major issue with storing comma separated fields.
MySQL does have a function to allow searching for these, and this will be a lot more reliable than trying to rely on using LIKE. But as it still cannot effectively use any index it will not be fast.
SELECT *
FROM offers
WHERE FIND_IN_SET('1002', in_stores)
Far better to redesign your database to use an extra table with a row for each value for each id.
You "could" do it with a LIKE Query, but this is not recommended.
SELECT * FROM offers WHERE in_stores LIKE "%1002%".
As long as you don't have values bigger than 9999 this will work.
BUT when one of your stores has the id 11002 of 99991002 it will also return these unwanted values.
What you should do is transform your mysql table to a have a second table storeLocations or sth. else. This should only have the properties offer_id and store_id.
It will transform your data to:
`offers`
id | title
1 | Shoes
2 | Gloves
3 | Shades
4 | Hats
5 | Shoes
6 | Shirts
7 | Hats
`storeLocations`
offer_id, store_id
1 | 1002
1 | 1003
2 | 1020
2 | 1011
3 | 1002
3 | 1009
4 | 1010
4 | 1002
5 | 1220
6 | 1010
7 | 1002
Then you can select from it like
SELECT * FROM offers AS o
LEFT JOIN storeLocations as l ON (o.id=l.offer_id)
WHERE l.store_id = 1002;
Now when you insert data you don't have to use implode but insert as many rows into storeLocations as there are store_id's for that specific item.
For more info on that topic have a look here.
I think you should be using the MySQL LIKE operator. It searches the database column for strings specified in your query.
Try this query:
$sql = "SELECT * FROM offers WHERE in_store LIKE '%1002%'";
Check this link for more explanation: W3Schools - SQL LIKE Operator
I think that should do the trick.
i have a table in which a row contains following data. So i need to compare data among themselves and show which data has maximum count.for ex. my table has following fruits name. So i need to compare these fruits among themselves and show max fruit count first.
s.no | field1 |
1 |apple,orange,pineapple |
2 |apple,pineapple,strawberry,grapes|
3 |apple,grapes, |
4 |orange,mango |
i.e apple comes first,grapes second,pineapple third and so on. and these datas are entered dynamically, so whatever the values is entered dynamically it needs to compare among themselves and get max count
Great question.
This is a classical bad outcome of not having the data normalized.
I recommend you to read about Database Normalization, normalize your tables and see after that how easy it is to do this with simple SQL queries
If you need to run queries on column field 1, then why not consider normalization ? Otherwise it might keep on getting complex and dirty in future.
Your current table will look like this (for serianl number 1 only), Pk can be an autoincrement primary key.
Pk | s.no |fruitId|
1 | 1 |1 |
2 | 1 |2 |
3 | 1 |3 |
Your New Table of Fruits
PK |fruitName |
1 |Apple |
2 |Orange |
3 |Pineapple |
This also helps you to avoid redundancy.
Quick solution would be counting the amount of fruits where you insert/update the row and add a fruitCount column. You can then use this column to order by.
Zohaib has to correct solution though - if you have the time and possibility for such changes. And I definitely suggest you to read Tudor's link!
I am creating a search using MySQL & PHP on an existing table structure.
Multiple search keywords can be entered and the user can opt to either match ALL or ANY. The any form is not too difficult, but i am breaking my head on writing an efficient solution for the AND form.
The following is about the AND form, so all the search keywords must be found.
The 2 tables i have to work with (search in) have a structure as follows:
Table1
- item_id (non-unique)
- text
Table2
- item_id (unique)
- text_a
- text_b
- text_c
(The real solution will also have a 3rd table, but that is structure the same way as Table1. Table2 will have around 20 searchable columns)
Table1 can have multiple rows for each item_id with different text.
Consider having only 2 search keywords (can be more in real live), then both must exist in:
- both in a single row/column
or:
- in 2 different columns of maybe different tables.
or:
- in 2 different rows with the same item_id (in case of both keywords found in different rows of Table1)
All i could come up with are very intensive sub-queries but that would bring the server down or the response times would be huge.
As i am using PHP i could use intermediate queries and store the results for use in a later final query.
Anyone some good suggestions?
Edit: There where requests for real examples, so here it goes.
Consider the following 2 tables with data:
Table 1
+---------+-----------+-----------+-----------+-----------+
| item_id | t1_text_a | t1_text_b | t1_text_c | t1_text_d |
+---------+-----------+-----------+-----------+-----------+
| 1 | aaa bbb | NULL | ccc | ddd |
| 2 | aaa ccc | ddd | fff | ggg |
| 3 | bbb | NULL | NULL | NULL |
+---------+-----------+-----------+-----------+-----------+
Table2
+---------+----------+---------+
| item_id | sequence | t2_text |
+---------+----------+---------+
| 1 | 1 | kkk lll |
| 2 | 1 | kkk |
| 2 | 2 | lll |
| 3 | 1 | mmm |
+---------+----------+---------+
PS In the real database (which i can not change, so full text indexes or changes to table definition are not an option) Table1 has about 20 searchable columns and there are 2 tables like Table2. This should not make a difference to the solution, although it is something to consider from a performance perspective.
Example searches:
Keywords: aaa bbb
Should return:
- item_id=1. Both keywords are found in column t1_text_a.
Keywords: ccc ddd
Should return:
- item_id=1. "ccc" is found in t1_text_c, "ddd" is found in t1_text_d.
- item_id=2. "ccc" is found in t1_text_a, "ddd" is found in t1_text_b.
Keywords: kkk lll
Should return:
- item_id=1. Both keywords found in a single row of Table2 in column t2_text.
- item_id=2. Both keywords found in Table2, but in separate rows with the same item_id.
Keywords: bbb mmm
Should return:
- item_id=3. "bbb" is found in table1.t1_text_a, "mmm" is found in table2.t2_text.
My progress so far
I actually, for now, gave up on trying to catch this in mostly SQL.
What i did do is to create a query for each table retrieving any row that matches at least 1 of the search keywords. If there is only 1 search keyword the query uses a LIKE, otherwise a REGEXP 'keyword1|keyword2'.
These rows are put in a PHP array with the item_id as the index, and a concatenation of all the strings (searchable columns) as value. When finished retrieving all possible rows, i search the array for rows that match all keywords in the concatenated field.
Most likely not the best solution and it will not scale very well if the search will return many candidate rows with at least 1 match.
It's hard to provide you with a finite answer since you do not give a lot of details about your case.
But maybe this can give you a starting point:
SELECT * FROM table1 AS tbl1
INNER JOIN table2 AS tbl2
WHERE
tbl1.text LIKE %search_word1%
AND tbl1.text LIKE %search_word2%
AND tbl2.text_a LIKE %search_word1%
AND tbl2.text_a LIKE %search_word2%
AND tbl2.text_b LIKE %search_word1%
AND tbl2.text_b LIKE %search_word2%
AND tbl2.text_c LIKE %search_word1%
AND tbl2.text_c LIKE %search_word2%
You can adapt with JOIN, INNER JOIN, LEFT JOIN, RIGHT JOIN and the different LIKE and AND/OR statements to obtain the result you're looking for.
Google some join examples with LIKE statements for more details.
But as Tom H. said, it'd be better if you could post a more precise table structure and a real exemple of search terms...
I'm trying to create a table like this:
lives_with_owner_no from until under_the_name
1 1998 2002 1
3 2002 NULL 1
2 1997 NULL 2
3 1850 NULL 3
3 1999 NULL 4
2 2002 2002 4
3 2002 NULL 5
It's the Nermalization example, which I guess is pretty popular.
Anyway, I think I am just supposed to set up a dependency within MySQL for the from pending a change to the lives_with table or the cat_name table, and then set up a dependency between the until and from column. I figure the owner might want to come and update the cat's info, though, and override the 'from' column, so I have to use PHP? Is there any special way I should do the time stamp on the override (for example, $date = date("Y-m-d H:i:s");)? How do I set up the dependency within MySQL?
I also have a column that can be generated by adding other columns together. I guess using the cat example, it would look like:
combined_family_age family_name
75 Alley
230 Koneko
132 Furrdenand
1,004 Whiskers
Should I add via PHP and then input the values with a query, or should I use MySQL to manage the addition? Should I use a special engine for this, like MemoryAll?
I disagree with the nermalization example on two counts.
There is no cat entity in the end. Instead, there is a relation (cat_name_no, cat_name), which in your example has the immediate consequence that you can't tell how many cats named Lara exist. This is an anomaly that can easily be avoided.
The table crams two relations, lives_with_owner and under_the_name into one table. That's not a good idea, especially if the data is temporal, as it creates all kinds of nasty anomalies. Instead, you should use a table for each.
I would design this database as follows:
create table owner (id integer not null primary key, name varchar(255));
create table cat (id integer not null primary key, current_name varchar(255));
create table cat_lives_with (
cat_id integer references cat(id),
owner_id integer references owner(id),
valid_from date,
valid_to date);
create table cat_has_name (
cat_id integer references cat(id),
name varchar(255),
valid_from date,
valid_to date);
So you would have data like:
id | name
1 | Andrea
2 | Sarah
3 | Louise
id | current_name
1 | Ada
2 | Shelley
cat_id | owner_id | valid_from | valid_to
1 | 1 | 1998-02-15 | 2002-08-11
1 | 3 | 2002-08-12 | 9999-12-31
2 | 2 | 2002-01-08 | 2001-10-23
2 | 3 | 2002-10-24 | 9999-12-31
cat_id | name | valid_from | valid_to
1 | Ada | 1998-02-15 | 9999-12-31
2 | Shelley | 2002-01-08 | 2001-10-23
2 | Callisto | 2002-10-24 | 9999-12-31
I would use a finer grained date type than just year (in the nermalization example having 2002-2002 as a range can really lead to messy query syntax), so that you can ask queries like select cat_id from owner where '2000-06-02' between valid_from and valid_to.
As for the question of how to deal with temporal data in the general case: there's an excellent book on the subject, "Developing Time-Oriented Database Applications in SQL" by Richard Snodgrass (free full-text PDF distributed by Richard Snodgrass), which i believe can even be legally downloaded as pdf, Google will help you with that.
Your other question: you can handle the combined_family_age either in sql externally, or, if that column is needed often, with a view. You shouldn't manage the content manually though, let the database calculate that for you.
I have a comma delimited list that im storing in a varchar field in a mysql table.
Is it possible to add and remove values from the list directly using sql queries? Or do I have to take the data out of the table, manipulate in PHP and replace it back into mysql?
There is no way to do it in InnoDB and MyIsam engines in mysql. Might be in other engines (check CSV engine).
You can do it in a stored procedure, but, not recommended.
What you should do to solve such an issue is to refactor your code and normalize your DB =>
original table
T1: id | data | some_other_data
1 | gg,jj,ss,ee,tt,hh | abanibi
To become:
T1: id | some_other_data
1 | abanibi
T2: id | t1_id | data_piece
1 | 1 | gg
2 | 1 | jj
3 | 1 | ss
4 | 1 | ee
5 | 1 | tt
6 | 1 | hh
and if data_piece is a constant value in the system which is reused a lot, you need to add there a lookup table too.
I know it looks more work, but then it will save you issues like you have now, which take much more time to solve.