MysQL (table1):
+----+--------+-------+--------+
| id | itemid | title | status |
+----+--------+-------+--------+
| 1 | 2 | title | 0 |
+----+--------+-------+--------+
| 2 | 2 | title | 1 |
+----+--------+-------+--------+
| 3 | 3 | title | 1 |
+----+--------+-------+--------+
| 4 | 3 | title | 0 |
+----+--------+-------+--------+
| 5 | 3 | title | 0 |
+----+--------+-------+--------+
MySQL (table2):
+----+---+---+
| id | x | y |
+----+---+---+
| id | 1 | 2 |
+----+---+---+
PHP:
(I know the query below makes no sense, but it should just illustrate what I am trying to do here.)
$a = mysql_query("SELECT t1.title FROM table1 AS t1 WHERE t1.title = 'title' ...IF t1.status = 1 THEN (SELECT * FROM table2 AS t2 WHERE t2.x = '1' AND t2.y = t1.itemid) ORDER BY `id`");
while($b = mysql_fetch_assoc($a))
{
echo $b['title'];
}
So, what I want to do is:
1) Get from table1 all rows that match title = title
2) If the status in table1 is equal to 0 do nothing, just display the data
3) However, if the status in table1 is equal to 1 then it should check if there is a record in table2 where x = 1 and y = itemid (from table1), if there isn't than the data from table1 should be excluded
Example:
In the example above, it should display ids (table1): 1, 2, 4, 5 ...3 should be excluded, because the status is 1, and there is no record that matches in table 2.
I hope this makes sense :/
you should use join for this.
Mysql join
or have a look how to use control flow functions in select statements:
Control flow functions
I would just like to assert that if you've got any if-then-else logic, any further processing of the data that you have stored in a database, use a programming language for that. There's nothing stopping you from writing all this logic in PHP, Java, C#, etc. There are far too many questions here about if-then-else stuff when retrieving data from database.
Some database management systems have their own dialect for programmatic SQL, be it PL/SQL or T-SQL for Oracle/SQL Server respectively... You could create your own modules with logic (packages, stored procedures) in your database and then use those, but then... do you really need to? Why could you not just implement all the data-presentation logic in your PHP script?
I'd like to expand on the previous answers a bit with some conceptual background: remember that SQL is declarative, not procedural. What this means is that you basically use SQL to tell the database what you want your result table to look like, and it figures out how to give it to you. When you're thinking about if/then statements and control logic, you're thinking in procedural terms, not declarative terms. This is why the previous answerer is suggesting to do your if/else logic in a 'programming language' like C# or PHP.
That's pretty abstract and so might not be directly applicable for you right this moment, but I find it helpful to compartmentalize things into 'procedural' and 'declarative' when I'm working with SQL and a scripting language (like PHP).
This StackOverflow question has some pretty good answers on the procedural vs. declarative concept.
Related
Below is a gross over simplification of 2 very large tables I'm working worth.
campaign table
| id | uid | name | contact | pin | icon |
| 1 | 7 | bob | ted | y6w | yuy |
| 2 | 7 | ned | joe | y6e | ygy |
| 3 | 6 | sam | jon | y6t | ouy |
records table
| id | uid | cid | fname | lname | address | city | phone |
| 1 | 7 | 1 | lars | jack | 13 main | lkjh | 55555 |
| 2 | 7 | 1 | rars | jock | 10 maun | oyjh | 55595 |
| 2 | 7 | 1 | ssrs | frck | 10 eaun | oyrh | 88595 |
The page loops thru the records table and prints the results to an HTML table. The existing code, for some reason, does a separate query for each record "select name from campaign where id = $res['cid']" I'd like to get rid of the second query and do a some kind of join but what is the most effective way to do it?
I need to
SELECT * FROM records
and also
SELECT name FROM campaigns WHERE campaigns.id = records.cid
in a single query.
How can I do this efficiently?
Simply join the two tables. You already have the required WHERE condition. Select all columns from one but only one column from the other. Like this:
SELECT records.*, campaigns.name
FROM records, campaigns
WHERE campaigns.id = records.cid
Note that a record row without matching campaign will get lost. To avoid that, rephrase your query like this:
SELECT records.*, campaigns.name
FROM records LEFT JOIN campaigns
ON campaigns.id = records.cid
Now you'll get NULL names instead of missing rows.
The "most efficient" part is where the answer becomes very tricky. Generally a great way to do this would be to simply write a query with a join on the two tables and happily skip away singing songs about kittens. However, it really depends on a lot more factors. how big are the tables, are they indexed nicely on the right columns for the query? When the query runs, how many records are generated? Are the results being ordered in the query?
This is where is starts being a little bit of an art over science. Have a look at the explain plan, understand what is happening, look for ways to make it more efficient or simpler. Sometimes running two subqueries in the from clause that will generate only a subset of data each is much more efficient than trying to join the entire tables and select data you need from there.
To answer this question in more detail, while hoping to be accurate for your particular case will need a LOT more information.
If I was to guess at some of these things in your database, I would suggest the following using a simple join if your tables are less than a few million rows and your database performance is decent. If you are re-running the EXACT query multiple times, even a slow query can be cached by MySQL VERY nicely, so look at that as well. I have an application running on a terribly specc'ed machine, where I wrote a cron job that simply runs a few queries with new data that is loaded overnight and all my users think the queries are instant as I make sure that they are cached. Sometimes it is the little tricks that really pay off.
Lastly, if you are actually just starting out with SQL or aren't as familiar as you think you might eventually get - you might want to read this Q&A that I wrote which covers off a lot of basic to intermediate topcs on queries, such as joins, subqueries, aggregate queries and basically a lot more stuff that is worth knowing.
You can use this query
SELECT records.*, campaigns.name
FROM records, campaigns
WHERE campaigns.id = records.cid
But, it's much better to use INNER JOIN (the new ANSI standard, ANSI-92) because it's more readable and you can easily replace INNER with LEFT or other types of join.
SELECT records.*, campaigns.name
FROM records INNER JOIN campaigns
ON campaigns.id = records.cid
More explanation here:
SQL Inner Join. ON condition vs WHERE clause
INNER JOIN ON vs WHERE clause
SELECT *
FROM records
LEFT JOIN campaigns
on records.cid = campaigns.id;
Using a left join instead of inner join guarantees that you will still list every records entry.
I got 3 tables in mysql:
cards:
id | name
1 | alpha
2 | beta
tags:
id | name
1 | a
2 | b
tag_link:
id | card | id
1 | 1 | 1
2 | 2 | 1
3 | 2 | 2
I would like to retrieve all cards which DON'T include a certain tag. CI model:
function search($_tag) {
$this->db->select('card.id');
$this->db->join('tag_link', 'card.id = tag_link.card');
$this->db->where_not_in('tag_link.tag', $_tag);
$this->db->group_by('card.id');
$query = $this->db->get('card');
return $query;
}
For tag '2' this returns card '1' as expected. However, card '2' is wrongly returned as well due to the one entry in tag_link connecting card '2' with tag '1'.
I thought about getting a first array of hits using the above function and then subtract another array in php containing all the cards including the tag I'm not interested in.
However this solution feels very clumsy. What's the most efficient approach for this problem?
Thanks,
singultus
I don't know too much about CI, but as an SQL query, this might work (pseudo code mixing SQL and php):
SELECT id
FROM cards
WHERE id NOT IN
(
SELECT card FROM tag_link WHERE tag IN $tags
)
The subquery returns all card ids that include a certain tag. The main query then returns all the other card ids. (Note that subqueries can cause performance issues in very large tables and/or complicated queries)
Using TheWolf 's answer and converting it to active_record. Remember sub-queries are not supported by CI (although library can be used)
$this->db->select('id');
$this->db->where('id NOT IN (SELECT card FROM tag_link WHERE tag IN $tags)', NULL, FALSE);
$query = $this->db->get('cards');
First of all your table structure should have clear names link this
cards:
-------
id | name
1 | alpha
2 | beta
tags:
------
id | name
1 | a
2 | b
tag_link:
---------
id | card_id | tag_id
1 | 1 | 1
2 | 2 | 1
3 | 2 | 2
Then you can put an and condition to avoid other results. and provide the avoiding card. Otherwise answering question is useless.
function search($tag_id,$card_id) {
return $this->db
->select('card.id')
->from('tag_link')
->join('card','card.id = tag_link.card_id','INNER')
->where_not_in('tag_link.tag_id',$_tag)
->where_not_in('tag_link.card_id',$card_id)
->get()
->result_array()
}
This will generate this query.
SELECT
card.id
FROM tag_link
INNER JOIN card
ON card.id = tag_link.card_id
WHERE tag_link.tag_id NOT IN(2)
AND tag_link.card_id NOT IN(2)
As you see you are avoiding card 2 other wise you are bound to get all the cards that are linked to any id anyway regards less of the tag_id you provide.
Here is the fiddle to test
I know that this title is overused, but it seems that my kind of question is not answered yet.
So, the problem is like this:
I have a table structure made of four tables (tables, rows, cols, values) that I use to recreate the behavior of the information_schema (in a way).
In php I am generating queries to retrieve the data, and the result would still look like a normal table:
SELECT
(SELECT value FROM `values` WHERE `col` = "3" and row = rows.id) as "col1",
(SELECT value FROM `values` WHERE `col` = "4" and row = rows.id) as "col2"
FROM rows WHERE `table` = (SELECT id FROM tables WHERE name = 'table1')
HAVING (col2 LIKE "%4%")
OR
SELECT * FROM
(SELECT
(SELECT value FROM `values` WHERE `col` = "3" and row = rows.id) as "col1",
(SELECT value FROM `values` WHERE `col` = "4" and row = rows.id) as "col2"
FROM rows WHERE `table` = (SELECT id FROM tables WHERE name = 'table1')) d
WHERE col2 LIKE "%4%"
note that the part where I define the columns of the result is generated by a php script. It is less important why I am doing this, but I want to extend this algorithm that generates the queries for a broader use.
And we got to the core problem, I have to decide if I will generate a where or a having part for the query, and I know when to use them both, the problem is my algorithm doesn't and I have to make a few extra checks for this. But the two above queries are equivalent, I can always put any query in a sub-query, give it an alias, and use where on the new derived table. But I wonder if I will have problems with the performance or not, or if this will turn back on me in an unexpected way.
I know how they both work, and how where is supposed to be faster, but this is why I came here to ask. Hopefully I made myself understood, please excuse my english and the long useless turns of phrases, and all.
EDIT 1
I already know the difference between the two, and all that implies, my only dilemma is that using custom columns from other tables, with variable numbers and size, and trying to achieve the same result as using a normally created table implies that I must use HAVING for filtering the derived tables columns, at the same time having the option to wrap it up in a subquery and use where normally, this probably will create a temporary table that will be filtered afterwards. Will this affect performance for a large database? And unfortunately I cannot test this right now, as I do not afford to fill the database with over 1 billion entries (that will be something like this: 1 billion in rows table, 5 billions in values table, as every row have 5 columns, 5 rows in cols table and 1 row in tables table = 6,000,006 entries in total)
right now my database looks like this:
+----+--------+-----------+------+
| id | name | title | dets |
+----+--------+-----------+------+
| 1 | table1 | Table One | |
+----+--------+-----------+------+
+----+-------+------+
| id | table | name |
+----+-------+------+
| 3 | 1 | col1 |
| 4 | 1 | col2 |
+----+-------+------+
where `table` is a foreign key from table `tables`
+----+-------+-------+
| id | table | extra |
+----+-------+-------+
| 1 | 1 | |
| 2 | 1 | |
+----+-------+-------+
where `table` is a foreign key from table `tables`
+----+-----+-----+----------+
| id | row | col | value |
+----+-----+-----+----------+
| 1 | 1 | 3 | 13 |
| 2 | 1 | 4 | 14 |
| 6 | 2 | 4 | 24 |
| 9 | 2 | 3 | asdfghjk |
+----+-----+-----+----------+
where `row` is a foreign key from table `rows`
where `col` is a foreign key from table `cols`
EDIT 2
The conditions are there just for demonstration purposes!
EDIT 3
For only two rows, it seems there is a difference between the two, the one using having is 0,0008 and the one using where is 0.0014-0.0019. I wonder if this will affect performance for large numbers of rows and columns
EDIT 4
The result of the two queries is identical, and that is:
+----------+------+
| col1 | col2 |
+----------+------+
| 13 | 14 |
| asdfghjk | 24 |
+----------+------+
HAVING is specifically for GROUP BY, WHERE is to provide conditional parameters. See also WHERE vs HAVING
I believe the having clause would be faster in this case, as you're defining specific values, as opposed to reading through the values and looking for a match.
See: http://database-programmer.blogspot.com/2008/04/group-by-having-sum-avg-and-count.html
Basically, WHERE filters out columns before passing them to an aggregate function, but HAVING filters the aggregate function's results.
you could do it like that
WHERE col2 In (14,24)
your code WHERE col2 LIKE "%4%" is bad idea so what about col2 = 34 it will be also selected.
I have two tables in my database
table: products table: companies
+-----------+------------+ +------+------------+
| name | company_id | | id | name |
+-----------+------------+ +------+------------+
|Product 1 | 1 | | 1 | Company 1 |
|Product 2 | 1 | | 2 | Company 2 |
|Product 3 | 3 | | 3 | Company 3 |
|Product 4 | 1 | | 4 | Company 4 |
|Product 5 | 1 | | ... |
|Product 6 | 3 | +------+------------+
|Product 7 | 2 |
|... |
+-----------+------------+
Now I have to make company-selector (filtering products by company) using HTML SELECT element with names of all companies from table companies and COUNT of products after company name in the list.
So, my goal is to get SELECT options like this:
Company 1 (4)
Company 2 (1)
Company 3 (2)
Company 4 (0)
(note: counts inside the brackets are from example above)
What have I tried so far?
I was using mysql_* functions earlier and later it was mysqli procedural model. I can do this manually with one query for companies and another one inside while block to get COUNT of elements (filtered by current company's id in the loop). Now I'm trying to work with PDO object which is something new for me and I'm not very familiar with it.
Question
Is it somehow possible to get COUNT with one query (using JOINs or something)? If not, how I can do it with query inside the loop (old way) using my $dbPDO object? Or any other way?
I've looked some examples here but nothing could adapt to fit my requirements. Maybe I missed something but working with PDO is still painful for me (and I must learn it ASAP).
Looking at my own question, I don't think it's something hard, but the worst thing, I can't find solution by myself.
At the end, I have solution in mysqli, just I don't like it so much and think there's easier way of making this task done!
I thought this question is something I need but still don't understand that query in answer.
So far I have this code and have no idea how to make it counts products:
$dbPDO = new PDO('mysql:dbname=comixdb;host=localhost', 'root', '');
$sel_cat = ''; # selector HTML
$sql = "SELECT * FROM categories ORDER BY name";
foreach ($dbPDO->query($sql) as $row) {
$sel_cat .= "<option value=\"{$row['id']}\">{$row['name']}</option>";
}
$sel_cat = "<select><option value=\"*\">All categories</option>$sel_cat</select>";
echo $sel_cat;
Hope I've clarified question enough. Any help would be appreciated.
What you need can be done in SQL:
SELECT companies.name, SUM(IF(company_id IS NULL, 0, 1)) AS products
FROM companies
LEFT JOIN products ON (companies.id = products.company_id)
GROUP BY companies.id;
The LEFT JOIN ensures that all companies get selected, and the SUM ensures that the count is correct (a simple COUNT would return 1 for companies with no products).
I have a MySQL Database for a basic quiz program I'm making. The questions table is as follows:
ID | quiz_ID | QuestionNumber | Question |
1 | 1 | 1 | What is 5 + 5? |
2 | 1 | 2 | What is 12 - 4? |
3 | 1 | 3 | Whats the square root of 25? |
4 | 2 | 1 | What is the centre of an atom called? |
5 | 2 | 2 | What is the nucleus made up of? |
What I need to happen is if $quiz_ID in my PHP equals 2, and $qn (which is the question number) equals 1, then it should display "What is the centre of an atom called?" And then I should be able to increment $qn and that should display the next question in that quiz. I just need some of the basic PHP code to enable me to do this.
So far, I can get it to find out how many questions are in each quiz (with a query), and then loop through them, but this only works for the very first quiz (quiz_ID = 1) I need it to be able to work with what ever quiz the user selected.
I also have 2 more tables, for quizzes and answers that are linked to this table, and when (if i ever get there) this program is up and running there will be loads more entries than there is in the example above, so I need to find a pretty solid algorithm thats going to work.
Thanks in advance, I am so grateful to you guys on this website - when I'm a bit better at PHP I will contribute and answer questions.
Thanks!
you need basic SQL for this, not PHP.
To query a row based on several criteria, just add them all into WHERE clause
SELECT * FROM questionsTbl WHERE quiz_ID = 1 AND QuestionNumber = 1
Notice AND operator. it tells MySQL to find a row which meets BOTH conditions.
And PHP has VERY little to do here. It's just adding several variables into text string - an operation you already familiar with.
For getting all the questions under the given quiz:
$quizID = 2;
$query="SELECT * FROM questionsTbl WHERE quiz_ID = $quizID
ORDER BY QuestionNumber";
$result = mysql_query($query) or die(mysql_error());
while($question = mysql_fetch_assoc($result){
...
...
}