I am trying to handle as much as possible inside the query since this is the fastest way of listing things in my current project.
Here's what I'm trying to do.
I have a table with stations:
id | station_call | station_band
--------+-----------------+-------------
1 | WABC | FM
2 | WXYZ | AM
Now normally, upon getting the resutls, it would be easy to just join the two with PHP to get the full station name
$row["station_call"] . "-" . $row["station_band"];
would result in WACB-FM and WXYZ-AM
Is there a way I can get this joining of the two inside the query?
Basically returning a new row, something like station_name and have the name already formated as WACB-FM
Bonus:
This probably makes it a bit harder, my query is also getting these results inside of a JOIN statement and processed as a GROUP_CONCAT()
Right now, I have two get separate GROUP_CONCATS() to return as two separate columns in each row resulting in "WABC, WXYZ" and "FM, AM" and having to explode the strings and join them based on index
Basically, I need it to be returned as a series of station names separated as a comma.
So when I get the final row, I'm trying to just reference $row["stations"] and get "WABC-FM, WXYZ-AM"
SELECT concat(station_call, '-', station_band) AS station_info FROM stations;
You need the mysql-concat function.
The Solution within a group_concat-functions (Bonus-Part of question) looks like this:
SELECT GROUP_CONCAT(CONCAT(s.radiostation_call, '-', s.radiostation_band) SEPARATOR '|') AS station_info FROM stations GROUP BY ID
I have a database containing more than 100,000 values. The structure looks something like as follows:
id | countryid | webid | categoryid | title | dateaddedon
If I use basic RAND() considering there are so many ids it won't be able to return a random result. I end up seeing titles of same webid next to each other. I would rather want titles from different webids being displayed. Therefore I figured since there are only 4-5 different values of webid it might be a better option to randomize the output based on this. I am unable to figure out how to define which specific column values should be randomized when using mysql SELECT command.
I am current using following
SELECT * FROM table WHERE countryid='1' ORDER BY dateaddedon DESC, RAND(3)
I am currently using 3 as seed value. I am not sure what kind of impact does seed value have on RAND. I would highly appreciate if someone could explain that too.
If seed value is specified it produces a repeatable sequence of column values. Unless you require a repeatable value leave it out. Also if you should have the RAND() as the first clause in ORDER.
SELECT * FROM table WHERE countryid='1' ORDER BY RAND(),dateaddedon DESC
About
I have this table in my database holding some information saved with a user id and time.
| CONTENT | USER ID | TIME |
| text | 1 | 1405085592 |
| hello | 2 | 1405085683 |
| hey | 1 | 1405086953 |
This example could be a data dump from my database, now as you can count there is "three" rows. However I only need to know how many users there have some information in my database. Therefor the result I'm really looking for is "two", because only two users have information in the database. User ID 1 is owning both "text"(1) & "hey"(3) where user ID 2 haves "hello"(2).
In short
I want to count how many users (regardless how many rows of information they have) there are inside my database.
** What I tried **
I tried to fetch every single row into an array and then using array_unique to count them together, works fine but I do not see this as a clean and best way to do this.
Then what?
I could use the array_unique and just use count to see how many rows there are, but I'm looking for something more clean. I tried to search for this, but I'm not actually sure what I should search for in term to hit something I'm looking for. After being stuck and though I wanted to learn something new, I wanted to post this problem here.
Note
I hope you guys can help me, I have tried to make it clear what I'm looking for and what I tried. If not please let me know. Sorry if some of the above contains misspelled words, incorrect grammar or is badly explained. I do not speak English daily, but I try my best.
You are looking for the DISTINCT keyword. It returns the count of unique values of a column:
SELECT COUNT(DISTINCT user_id)
FROM your_table
See example on SQL Fiddle.
This query:
SELECT DISTINCT user_id FROM table
will return just one row for every user in the table.
I apologize in advance for the long question. I am designing a webpage for a DNA research lab and I am stuck on one particular point. The webpage accesses a single MySQL database, but the database contains dozens of tables. Each table corresponds to one experiment. The tables each follow the same general format: one column lists DNA gene names and the next column displays the amount of the DNA gene present. However, each table contains a different set of genes (the genes in one experiment aren't always the same as from another experiment).
At this point, I want the user to input which gene he is interested in and then the webpage will display which experiments have data for that gene. Basically, I need to figure out which MySQL tables in the database have the data that I want.
The way I see it, I need to cycle through each table in the MySQL database and do a SELECT WHERE query on each table. If it returns something, it is a table that I want and I will add the table name to an array. If not, I just move on to the next table.
What is the best way to do this and what languages do I need? I will use HTML and PHP for the webpage and MySQL for the database queries. However, what can I use to cycle through the tables? I was thinking javascript or ASP?
Let's assume that you can't change your database structure. You can get a list of all of the tables in your database using the query:
SHOW TABLES
Next, you need to know which tables are for experiments you care about. You'll probably have to do some kind of string matching -- hopefully they have names that start with "experiment_" or something.
Then you just run a SELECT statement looking for that gene in the table. Finally, you somehow map the experiment names to the table names, and display those experiment names. The code would be something like:
$result = mysql_query("show tables");
$tables = array();
while ($row = mysql_fetch_array($result)) {
// Determine whether this is an experiment table.
if (preg_match("/^experiment_/", $row[0])) {
$tables[] = $row[0];
}
}
$tables_with_gene = array();
// As you can see, every search runs bunches of queries.
foreach ($tables as $table_name) {
$result = mysql_query("select gene_name from $table where gene_name = '$gene_name'");
if (mysql_num_rows($result)) {
$tables_with_gene[] = $table_name;
}
}
// Now you look up the experiment names
$experiment_names = array();
foreach ($tables_with_gene as $table_name) {
$result = mysql_query("select experiment_name from experiments where table_name = '$table_name'");
while ($row = mysql_fetch_array($result)) {
$experiment_names[] = $row[0];
}
}
At the end of all this, $experiment_names has a list of the experiments that include the gene in question.
Note that if the gene name is user input you'll want to sanitize it first to avoid SQL injection.
But yeah, you probably want one table that looks like:
experiment_id
gene_name
gene_frequency
Then you could do it all with one query:
SELECT e.experiment_name FROM experiment_data d JOIN experiments e
ON d.experiment_id = e.id
WHERE d.gene_name = 'your gene name'
It sounds like you may need to redesign your database? I think you only need one table, and the "gene set" that is currently distinguishing tables should be a non-unique key on that table.
Then, you should be able to query that single table WHERE the gene set equals the set you are looking for...
Since you are planning to use PHP then that is a good choice for performing the logic that you need.
Do you have control over the structure of the database? If you do, it may be easier to restructure the database itself to support the types of queries that you need. For instance, you can have a single table listing the experiments, another table listing the genes, and a third table connecting the experiment to the gene and the other data that goes with it. This would avoid all the searching through tables for data that you have to do. The advantage would then be that as more experiments are added the application would continue to work without modifying the PHP code.
You should really consider that redesign people have mentioned if at all possible. Your data format has some real problems. If it were not done this way you wouldn't have this problem. 28000 records is quite small in database terms and it doesn't matter if the gene is involved in more than one experiment. That's really the whole point of multiple fields in databases. They are meant to work with data of exactly that type. You just need another field denoting which experiment the data in the amount column refers to.
So rather than....
-----------------
| Gene | Amount |
-----------------
| abc | 123 |
| xyz | 789 |
-----------------
You have:
------------------------------
| Experiment | Gene | Amount |
------------------------------
| ex1 | abc | 123 |
| ex2 | abc | 456 |
| ex2 | xyz | 789 |
| ex1 | xyz | 058 |
------------------------------
etc, etc, etc
Then if you need to see just the data from ex1 it's:
SELECT *
FROM tblGeneData
WHERE Experiment = "ex1"
That query will give you the same results as:
SELECT *
FROM tblExperiment1
This is how relational databases are meant to work. They are not generally meant to keep the same type of data in two different tables just because there is a differentiating property.
EDIT:
I feel the need to also point out that you would generally also want an additional field to use as a unique key for the table. I would add an additional field called "Id" to the table and make it autonumber. You could use a compound key made up of your data but the generally accepted "best practice" is to have a separate unique key field that is meaningless outside the context of the inner workings of the database. This field would be used as the primary key for the table.
Here is the scenario 1.
I have a table called "items", inside the table has 2 columns, e. g. item_id and item_name.
I store my data in this way:
item_id | item_name
Ss001 | Shirt1
Sb002 | Shirt2
Tb001 | TShirt1
Tm002 | TShirt2
... etc, i store in this way:
first letter is the code for clothes, i.e S for shirt, T for tshirt
second letter is size, i.e s for small, m for medium and b for big
Lets say in my items table i got 10,000 items. I want to do fast retrieve, lets say I want to find a particular shirt, can I use:
Method1:
SELECT * from items WHERE item_id LIKE Sb99;
or should I do it like:
Method2:
SELECT * from items WHERE item_id LIKE S*;
*Store the result, then execute second search for the size, then third search for the id. Like the hash table concept.
What I want to achieve is, instead of search all the data, I want to minimize the search by search the clothes code first, follow by size code and then id code. Which one is better in term of speed in mysql. And which one is better in long run. I want to reduce the traffic and not to disturb the database so often.
Thanks guys for solving my first scenario. But another scenario comes in:
Scenario 2:
I am using PHP and MySQL. Continue from the preivous story. If my users table structure is like this:
user_id | username | items_collected
U0001 | Alex | Ss001;Tm002
U0002 | Daniel | Tb001;Sb002
U0003 | Michael | ...
U0004 | Thomas | ...
I store the items_collected in id form because one day each user can collect up to hundreds items, if I store as string, i.e. Shirt1, pants2, ..., it would required a very large amount of database spaces (imagine if we have 1000 users and some items name are very long).
Would it be easier to maintain if I store in id form?
And if lets say, I want to display the image, and the image's name is the item's name + jpg. How to do that? Is it something like this:
$result = Select items_collected from users where userid= $userid
Using php explode:
$itemsCollected = explode($result, ";");
After that, matching each item in the items table, so it would like:
shirt1, pants2 etc
Den using loop function, loop each value and add ".jpg" to display the image?
The first method will be faster - but IMO it's not the right way of doing it. I'm in agreement with tehvan about that.
I'd recommend keeping the item_id as is, but add two extra fields one for the code and one for the size, then you can do:
select * from items where item_code = 'S' and item_size = 'm'
With indexes the performance will be greatly increased, and you'll be able to easily match a range of sizes, or codes.
select * from items where item_code = 'S' and item_size IN ('m','s')
Migrate the db as follows:
alter table items add column item_code varchar(1) default '';
alter table items add column item_size varchar(1) default '';
update items set item_code = SUBSTRING(item_id, 1, 1);
update items set item_size = SUBSTRING(item_id, 2, 1);
The changes to the code should be equally simple to add. The long term benefit will be worth the effort.
For scenario 2 - that is not an efficient way of storing and retrieving data from a database. When used in this way the database is only acting as a storage engine, by encoding multiple data into fields you are precluding the relational part of the database from being useful.
What you should do in that circumstance is to have another table, call it 'items_collected'. The schema would be along the lines of
CREATE TABLE items_collected (
id int(11) NOT NULL auto_increment KEY,
userid int(11) NOT NULL,
item_code varchar(10) NOT NULL,
FOREIGN KEY (`userid`) REFERENCES `user`(`id`),
FOREIGN KEY (`itemcode`) REFERENCES `items`(`item_code`)
);
The foreign keys ensure that there is Referential integrity, it's essential to have referential integrity.
Then for the example you give you would have multiple records.
user_id | username | items_collected
U0001 | Alex | Ss001
U0001 | Alex | Tm002
U0002 | Daniel | Sb002
U0002 | Daniel | Tb001
U0003 | Michael | ...
U0004 | Thomas | ...
The first optimization would be splitting the id into three different fields:
one for type, one for size, one for the current id ending (whatever the ending means)
If you really want to keep the current structure, go for the result straight away (option 1).
If you want to speed up for results you should split up the column into multiple columns, one for each property.
Step 2 is to create an index for each column. Remember that mysql only uses one index per table per query. So if you really want speedy queries and your queries vary a lot with these properties, then you might want to create an index on (type,size,ending), (type,ending,size) etc.
For example a query with
select * from items where type = s and size = s and ending = 001
Can benefit from the index (type,size,ending) but:
select * from items where size = s and ending = 001
Can not, because the index will only be used in order, so it needs type, then size, then ending. This is why you might want multiple indexes if you really want fast searches.
One other note, generally it is not a good idea to use * in queries, but to select only the columns you need.
You need to have three columns for the model, size and id, and index them this way:
CREATE INDEX ix_1 ON (model, size, id)
CREATE INDEX ix_2 ON (size, id)
CREATE INDEX ix_3 ON (id, model)
Then you'll be able to search efficiently on any subset of the parameters:
model-size-id, model-size and model queries will use ix_1;
size-id and size queries will use ix_2;
model-id and id queries will use ix_3
Index on your column as it is now is equivalent to ix_1, and you can use this index to efficiently search on the appropriate conditions (model-size-id, model-size and model).
Actually, there is a certain access path called INDEX SKIN SCAN that may be used to search on non-first columns of a composite index, but MySQL does not support it AFAIK.
If you need to stick to your current design, you need to index the field and use queries like:
WHERE item_id LIKE #model || '%'
WHERE item_id LIKE #model || #size || '%'
WHERE item_id = #model || #size || #id
All these queries will use the index if any.
There is not need to put in into multiple queries.
I'm comfortable that you've designed your item_id to be searchable with a "Starts with" test. Indexes will solve that quickly for you.
I don't know MySQL, but in MSSQL having an index on a "Size" column that only has choices of S, M, L most probably won't achieve anything, the index won't be used because the values it contains are not sufficiently selective - i.e. its quicker to just go through all the data rather than "Find the first S entry in the index, now retrieve the data page for that row ..."
The exception is where the query is covered by the index - i.e. several parts of the WHERE clause (and indeed, all of them and also the SELECT columns) are included in the index. In this instance, however, the first field in the index (in MSSQL) needs to be selective. So put the column with the most distinct values first in the index.
Having said that if your application has a picklist for Size, Colour, etc. you should have those data attributes in separate columns in the record - and separate tables with lists of all the available Colours and Sizes, and then you can validate that the Colour / Size given to a Product is actually defined in the Colour / Size tables. Cuts down the Garbage-in / Garbage-out problem!
Your item_selected needs to be in a separate table so that it is "normalised". Don't store a delimited list in a single column, store it using individual rows in a separate table
Thus your USERS table will contain user_id & username
Your, new, items_collected table will contains user_id & item_id (and possibly also Date Purchased or Invoice Number)
You can then say "What did Alex buy" (your design has that) and also "Who bought Ss001" (which, in your design, would require ploughing through all the rows in your USERS table and splitting out the items_collected to find which ones contained Ss001 [1])
[1] Note that using LIKE wouldn't really be safe for that because you might have an item_id of "Ss001XXX" which would match WHERE items_collected LIKE '%Ss001%'