I want to retrieve rows from a table based on some search criteria. The results I get have to meet those criteria and should additionally deliver a sample of the data that is as diverse as possible. A query with the same search criteria should always return the same sample though. So using RAND() in the query is no solution. The results are used to be displayed on a PHP driven website.
Example
I've got a table with accommodations, e.g. hotels, Bed&Breakfast or campgrounds. For every accommodation, there are additional informations like rating, budget, city and region. So the table basically looks like this:
| id | name | type | rating | budget | city | region |
| 1 | A Name | Hotel | 2 | 2 | New York | East |
| 2 | B Name | B&B | 3 | 2 | New York | East |
| 3 | C Name | Hotel | 4 | 3 | New York | East |
| 4 | A Name | Hotel | 3 | 4 | Chicago | Central |
| 5 | D Name | B&B | 4 | 3 | Chicago | Central |
| 6 | E Name | Hotel | 2 | 2 | Omaha | Central |
| 7 | F Name | Hotel | 5 | 4 | Omaha | Central |
| 8 | G Name | Camping | 2 | 4 | Yosemite | West |
I now need a query that gets ten accommodations from e.g. region='Central' which contains as many cities as possible, as many accommodation types as possible, cheap accommodations as well as expensive ones and so on. I don't need a mathematically perfect solution, just something consistent.
Idea 1
I could query the table multiple times, with several different where clauses and mix the results. But querying multiple times is a big drawback for a web application.
Idea 2
I could introduce an additional column random that is filled by a random value while inserting the data and then do an order by random. The drawback of this solution is that random is a bad heuristic for what I want to accomplish.
Related
I am working on a project for recharge and bill payments. I am confused about whether to use a single table for all type of recharges like mobile recharge, dtn recharge, electricity bill, water bill, card recharges, etc, which is difficult or do I create separate tables for each type of recharge and work on them.
Table has colums
recharge_id PRIMARY KEY,
recharge_amount ,
recharge_status,
recharge_time,
user_id,
payment_id
The data has to be added into the table when there is any recharge process with status and other details.
Although, you didn't show anything you tried, i think this is a viable question.
A possible approach would be to create one table for your type and one for your recharges
Something like the following should work
create a table recharge_type like
+----+------------------+--------+
| id | name | active |
+----+------------------+--------+
| 1 | Mobile recharge | 1 |
| 2 | Dtn recharge | 1 |
| 3 | electricity bill | 1 |
| 4 | water bill | 1 |
| 5 | card recharge | 1 |
+----+------------------+--------+
and your table recharge
+----+------------------+---------+------------+--------+--------+------------+
| id | recharge_type_id | user_id | payment_id | amount | status | time |
+----+------------------+---------+------------+--------+--------+------------+
| 1 | 1 | 1 | 1 | 2.00 | 1 | 2019-03-05 |
| 2 | 2 | 1 | 2 | 3.00 | 3 | 2019-03-05 |
| 3 | 2 | 1 | 2 | 4.00 | 4 | 2019-03-05 |
+----+------------------+---------+------------+--------+--------+------------+
With this type of construction you are pretty flexible for nearly any approach.
If you want to understand why this is a good approach, you should read
some articles about first normal form. You can find an article
here on Wikipedia.
I've got a small CRM and I'm trying to figure out the best way to designing the DB tables.
I've currently got a single table for users that got around 30 columns which I alter from time to time. Since I am storing two different information on that table (user + company information) I was thinking of splitting that table into 3 (user + company + connection between these 2) but I am also interested in keeping a copy of any changes that are being made in these rows.
So going from:
user_id | firstname | last_name | company_name | company_city | company_subject | rank | status
1 | John | Borrows | Boink INC | NY | Web dev | 1 | 1
2 | Mike | Smith | Smithin INC | OC | Laywer | 1 | 2
3 | Mary | Anton | Caffin | SJ | Moving | 2 | 1
to something like this
user_id | firstname | last_name | rank | status
1 | John | Borrows | 1 | 1
2 | Mike | Smith | 1 | 2
3 | Mary | Anton | 2 | 1
comp_id | company_name | company_city | company_subject
1 | Boink INC | NY | Web dev
2 | Smithin INC | OC | Laywer
3 | Caffin | SJ | Moving
con_id | user_id | comp_id
1 | 1 | 1
2 | 2 | 2
3 | 3 | 3
But I'm not sure how to track the changes when for example a user changes the company name or some other info on user's table etc.
Just follow the normalization rules for structuring your database tables. You will find anything you need for that by just searching for database normalization.
Regarding your "update-history" you could add a Timestamp to your datasets and/or a separate boolean field "outdated" to be able to filter out the latest information.
Would be the simplest solution that comes into my mind.
I have a table called facility.
Structure looks as follows:
id | name
---------
1 | Hotel
2 | Hospital
3 | medical shop
I have an other table which is taking data from the above table and keeping multiple values in one column. View looks like below:
id | facilities
---------------
1 | Hospital~~medical shop~~Hotel
2 | Hospital~~Hotel
3 | medical shop~~Hotel
If I want to join these two tables how does the query look like?
I tried this, but it didn't work:
select overview.facilities as facility
from overview join facility on facility.id=overview.facilities;
you can do this with a bit of hackery
select o.facilities as facility
from overview o
join facility f on find_in_set(f.facilities, replace(o.facilities, '~~', ','));
I would highly recommend you change the way you are storing data. currently it is considered un normalized and that quickly becomes a monster to deal with
you should change your table structure to look something more like this
+----------+--------------+
| facility |
+----------+--------------+
| id | name |
+----------+--------------+
| 1 | Hotel |
| 2 | Hospital |
| 3 | medical shop |
+----------+--------------+
+-----------+-------------+
| overview |
+-----------+-------------+
| id | facility_id |
+-----------+-------------+
| 1 | 2 |
| 2 | 3 |
| 3 | 1 |
| 4 | 2 |
| 5 | 1 |
| 6 | 3 |
| 7 | 1 |
+-----------+-------------+
Code Explanation:
basically you are wanting to find the matching facilities in the overview. one handy function MySQL has is FIND_IN_SET() that allows you to find an item in a comma separated string aka find_in_set(25, '11,23,25,26) would return true and that matching row would be returned... you are separating your facilities with the delimiter ~~ which wont work with find_in_set... so I used REPLACE() to change the ~~ to a comma and then used that in the JOIN condition. you can go from here in multiple ways.. for instance lets say you want the facility id's for the overview.. you just add in the select GROUP_CONCAT(f.id) and you have all of the id's... note if you do that you need to add a GROUP BY at the end of your query to tell it how you want the results grouped
Okay, so lets say that we have 4 columns and 3 rows of data.
|user_id|pick_1|pick_2|pick_3|
-------------------------------
|fred |C++ |java | php |
------------------------------
|eric |java |C++ | php |
------------------------------
|sam | C++ | php | java |
------------------------------
So right now, users are entering their favorite languages. The first pick(pick_1) would be the favorite programming language and the second pick (pick_2) would be the 2nd favorite programming language and etc.
How can I organize this in a way so that I can give a point value according to what columns the programming languages are. So maybe pick_1 can give 3 points, pick_2 can give 2 points and pick_3 can give 1 point.
So when you tally up the scores, C++ will have 8 points, java will have 6 points, and php will have 4 points.
That way I can give an overall ranking of what tends to be the more favorable programming language. Like so
|rank|language|points|
----------------------
| 1 | C++ | 8 |
----------------------
| 2 | java | 6 |
----------------------
| 3 | php | 4 |
----------------------
It doesn't even need to have a point system, I just couldn't think of another way to rank the languages on a scale of liked to un-liked. So if there's another way to yield the same results than please let me know. Otherwise how would I be able to do this. Preferably in just MySql. I am currently using PHP.
Thank you for reading.
You need a simpler structure
User_ID | Pick | Points
Fred c++ 3
Fred php 2
Fred java 1
This way you can do a simple sum(points) group by pick
for a SQL only solution, I would normalize your structure, and put the picks in a different table:
users: user_id; user_name
picks: pick_id; user_id; language; points;
then you would have your data in 2 tables:
| user_id | user_name |
-----------------------
| 1 | Fred |
-----------------------
| 2 | Eric |
-----------------------
| 3 | Sam |
-----------------------
| pick_id | user_id | language | points |
---------------------------------------------
| 1 | 1 | C++ | 1 |
---------------------------------------------
| 2 | 1 | Java | 2 |
---------------------------------------------
| 3 | 1 | php | 3 |
---------------------------------------------
| 4 | 2 | Java | 1 |
---------------------------------------------
| 5 | 2 | C++ | 2 |
---------------------------------------------
| 6 | 2 | php | 3 |
---------------------------------------------
| 7 | 3 | C++ | 1 |
---------------------------------------------
| 8 | 3 | Java | 2 |
---------------------------------------------
| 9 | 3 | php | 3 |
---------------------------------------------
And then use the following query to fetch the desired result:
SELECT language, SUM(points) FROM users JOIN picks ON users.user_id=picks.user_id GROUP BY language
As seen in this fiddle
This way it's also easy to add constraints so people can not vote for a language more then once, or give the same amount of votes to 2 different languages.
i have a table in following format:
id | title
---+----------------------------
1 | php jobs, usa
3 | usa, php, jobs
4 | ca, mysql developer
5 | developer
i want to get the most popular keywords in title field, please guide.
If you have a list of keywords, you can do the following:
select kw.keyword, count(*)
from t cross join
keywords kw
on concat(', ', t.title, ',') like concat(', ', kw.keyword, ',')
As others have mentioned, though, you have a non-relational database design. The keywords in the title should be stored in separate rows, rather than as a comma separated list.
If your data is small (a few hundred thousand rows or less), you can put it into Excel, use the text-to-columns function, rearrange the keywords, and create a new, better table in the database.
SELECT title 1, COUNT(*) FROM table GROUP BY title 1
EDIT
Since you've edited and presented a non-normalized table, I would recommend you normalize it.
Have a read of: http://blog.fedecarg.com/2009/02/22/mysql-split-string-function/
You need to modify your database. You should have something like this:
items
+----+---------------+
| id | title |
+----+---------------+
| 1 | something |
| 3 | another thing |
| 4 | yet another |
| 5 | one last one |
+----+---------------+
keywords
+----+-----------------+
| id | keyword |
+----+-----------------+
| 1 | php jobs |
| 2 | usa |
| 3 | php |
| 4 | jobs |
| 5 | ca |
| 6 | mysql developer |
| 7 | developer |
+----+-----------------+
items_to_keywords
+---------+------------+
| item_id | keyword_id |
+---------+------------+
| 1 | 1 |
| 1 | 2 |
| 3 | 2 |
| 3 | 3 |
| 3 | 4 |
| 4 | 5 |
| 4 | 6 |
| 5 | 7 |
+---------+------------+
Do you see the advantage? The ability to make relations is what you should be leveraging here.