I have a table that looks somewhat like the one below, and I am doing a search query with multiple fields to refine a search for a job. At the moment I am able to enter into multiple fields, however, my search results query the entire database, not just for one specific ID.
Table:
contentId meta_key meta_value
1 1 vacancyType Hospitality
2 1 vacancyRole Chef
3 1 vacancyDate 2014-01-01
4 2 vacancyType Adin
5 2 vacancyArea St Albans
6 2 vacancyDate 2014-01-01
Code:
$getJobs1 = "SELECT distinct *
FROM cms_contentExtra, cms_content
WHERE cms_contentExtra.meta_value IN ('$type','$key')
AND cms_content.contentId = cms_contentExtra.contentId
GROUP BY cms_content.contentId";
$getJobs2 = mysql_query($getJobs1) or die("didn't query");
while ($getJobs3 = mysql_fetch_array($getJobs2)) {
echo ' - ' . $getJobs3[meta_value] . ' - ' . ' - ' . $getJobs3[contentId];
}
This will return:
- St Albans - - 8435 - St Albans - - 8436 - Hospitality & Catering - - 8437 - St Albans - - 8440 - Hospitality & Catering - - 8444 - Hospitality & Catering - - 8450 - Hospitality & Catering - - 8451 - St Albans - - 8453
However, I only want to show results that have BOTH the area and type, and disregard the others. At the moment it seems to be displaying the type and then displaying the area separately, so I am unable to show a type in a specific area.
I am using two tables to compare the contentId to more data stored in a different table.
The IN operand that you use is an OR operation.
Try to use LIKE such as:
WHERE cms_contentExtra.meta_value LIKE "%$type%$key%"
OR cms_contentExtra.meta_value LIKE "%$key%$type%"
Let's have a closer look at your query. You group by contentId. So you get one record per contentId. But you select *. (This must be MySQL, for I don't know any other dbms allowing this.) So you get the content record belonging to the content id, and one (random) cms_contentExtra record belonging to the content id. Then you use DISTINCT, so you only get distinct rows. But the rows must be distinct, because they differ at least in the content id. Having said this, your query doesn't make sense, and I recommend you look up GROUP BY and DISTINCT in a book or tutorial. (Moreover you shouldn't use comma-separated join syntax. That was replaced more than twenty years ago with explicit joins. Use INNER JOIN instead.) So far to this :-)
To clean things up for you, I lead you step by step. Maybe this is not exactly what you are looking for, but I think it will help you build the final query.
To get content IDs with the vacancy type $type:
select contentid
from cms_content
where meta_key = 'vacancyType' and meta_value = #type;
To get content IDs with the location $key:
select contentid
from cms_content
where meta_key = 'vacancyArea' and meta_value = #key;
To get content IDs that match $type or $key:
select distinct contentid
from cms_content
where (meta_key = 'vacancyType' and meta_value = #type)
or (meta_key = 'vacancyArea' and meta_value = #key);
To get content IDs that match both $type and $key:
select contentid
from cms_content
where (meta_key = 'vacancyType' and meta_value = #type)
or (meta_key = 'vacancyArea' and meta_value = #key)
group by contentid
having count(*) = 2;
Related
I've 4 table for a newsletter. Newsletters, Subscribers, Subscriber Groups and Selected Subscriber Groups. I've choose subscriber groups in campaign edit area, and its save selected groups to tbl_newsletter_groups table like;
tbl_newsletters
NID title details
1 text 1 content 1
2 text 2 content 2
tbl_subscriber_groups
GID group_name
5 group 1
6 group 2
tbl_subscribers
SID GID email name
10 5 sub1#mail.com sub1 name
11 6 sub1#mail.com sub1 name
tbl_newsletter_groups
NGID NID GID
15 1 6
16 1 6
17 1 6
I want to show total selected subscriber count when I list newsletters in my page. My soulution works fine, Im looking for simple and clearly statement, there any faster way available like in single newsletter list statement?
Here my own count style (yes I know its too bad and long way);
$subGID = array();
$list = $myconn->query("SELECT * FROM tbl_newsletters");
while($listRs = $list->fetch_assoc()){
$grps = $myconn->query("SELECT * FROM tbl_newsletter_groups WHERE NID=". $listRs['NID'] ."");
while($grpsRs = $grps->fetch_asscoc()){
$subGID[] = $grpsRs['GID'];
} $grps->free();
$subs = implode(" OR GID=",$subGID);
$count = mysqli_num_rows($myconn->query("SELECT ID FROM tbl_subscribers WHERE GID=". $subs));
echo('Total Selected Subscriber: '.$count);
} $list->free();
Thanks.
The search term you want is "set-based logic".
Your thinking is sound: you need everything from tbl_newsletters, then you need to count results from tbl_subscribers, but in order to get those you need information from tbl_newsletter_groups.
In SQL, that's an indication you want a join. You've already discovered the conditions you need, you just don't know the syntax. A reference manual can help there.
Now you'll have a bunch of records, which you need to smash into a smaller number of records. You need aggregation functions and a GROUP BY clause.
So here's the final query:
SELECT n.NID, n.title, n.details, COUNT(s.SID)
FROM tbl_newsletters AS n
JOIN tbl_newsletter_groups AS g ON n.NID = g.NID
JOIN tbl_subscribers AS s ON g.GID = s.GID
GROUP BY n.NID
We have users, questions and unlimited levels of categories. The users can get some points from questions. Questions can have multiple categories.
What I want to do is to calculate the top users per category: It's simply total points taken from the questions under that category AND it's sub-categories too.
So, I have these tables:
questions
--------------
id
title
question
categories
--------------
id
parent_id
category
lft
rgt
question_categories
--------------
question_id
category_id
users
--------------
id
username
user_points
--------------
id
user_id
question_id
point_type
points
user_category
--------------
user_id
category_id
points
What I want to do is to calculate user_category.points value.
Summing up the points for each category is easy but including the sub-categories is getting complicated.
What might be the best way to do this?
Example calculation:
Let's say the categories are:
Programming
PHP
Zend Framework
Symfony
Java
Ruby on Rails
Assume that the user got 3 points from Zend Framework, 2 points from PHP, 5 points from java and 1 point from Rails. The points for this user per categories will be:
Programming 11 (5+5+1)
PHP 5 (2+3)
Zend Framework 3
Symfony
Java 5
Ruby on Rails 1
Perhaps it would be best to use tags instead of a hierarchy. For instance, anything with a "Zend Framework" will also have "PHP" and "Programming" tags. This also helps when some categories can appear in multiple places. For instance, I can use ajax in jQuery and also Javascript. Then, add 1 to each tag listed in the category for the user.
I would create a user_categories table in which I would store 3 values: user_id, category_id and user_score. It's easy to maintain (need only to INSERT or UPDATE) and it's also easy to query for top-users of every category.
If you're only going to sum per top-level category, then you should add a field to your categories table called root_id (holding the id of the transitive parent of the category).
Then your sum would be calculated as:
select up.user_id, ctg.root_id, sum(up.points)
from user_points up
join question_categories qc on up.question_id = qc.question_id
join categories ctg on qc.category_id = ctg.id
group by up.user_id, ctg.root_id
This php and SQL should get you the top 3 users for each category including sub categories:
$query = "SELECT id, parent_id FROM categories";
$parent = array();
...fetch mysql data loop depending on what connection you use, mysqli or pdo...
{
$parent[$result['id']] = $result['parent_id'];
}
$childs = array();
foreach($parent as $id => $parrent_id)
{
$childs[$parrent_id][$id] = $id;
$next_parrent_id = $parrent_id;
while($next_parrent_id = $parent[$next_parrent_id])
{
$childs[$next_parrent_id][$id] = $id;
}
}
foreach($parent as $id => $parrent_id)
{
$current_categories = array($id => $id) + $childs[$id];
$query = "SELECT user_id, username, SUM(points) AS total_points
FROM user_points
LEFT JOIN users ON (user_id = users.id)
LEFT JOIN question_categories USING (question_id)
WHERE category_id IN (" . implode(', ', $current_categories). ")
ORDER BY total_points DESC
LIMIT 3";
...fetch mysql data loop...
}
Here is the mySQL I got
id terms
1 a
2 c
3 a
4 b
5 b
6 a
7 a
8 b
9 b
10 b
I want to get an alphabetized list sorted by count as follows
terms count
a 4
b 5
c 1
What mySQL statement do I need for that?
I believe something like this will work:
SELECT terms, COUNT( id) AS count
FROM table
GROUP BY terms
ORDER BY terms DESC
Read : GROUP BY (Transact-SQL)
Groups a selected set of rows into a set of summary rows by the values of one or more columns or expressions in SQL. One row is returned for each group. Aggregate functions in the SELECT clause list provide information about each group instead of individual rows.
You just need to apply group by clause for getting result
select terms, count (id) as count from table
group by terms
order by terms
I had a very similar need for a used record store to display artists in stock alphabetically with their count in parenthesis e.g.:
Smokey Robinson and The Miracles (2) | Sonic Youth (2) | Spoon (3) | Steely Dan (1) | Stevie Wonder (2) | Sufjan Stevens (1) |
Note that I used SELECT DISTINCT when pulling from my table "records". Here are the relevant code snippets:
//QUERY
$arttool = mysql_query("SELECT DISTINCT * FROM records GROUP BY artist ORDER BY artist ASC");
//OUTPUT LOOP START
while($row = mysql_fetch_array($arttool)){
//CAPTURE ARTIST IN CURRENT LOOP POSITION
$current=$row['Artist'];
//CAPTURING THE NUMBER OF ALBUMS IN STOCK BY CURRENT ARTIST
$artcount = mysql_num_rows(mysql_query("SELECT * FROM records WHERE artist = '$current'"));
//ECHO OUT.
echo $current . "($artcount)";
The actual code in my site is more complicated, but this is the bare bones of it. Hope that helps...
I am just getting started in learning how to do INNER JOINS correctly and I can't think of the best/easiest way to do this.
I am building a url shortener and I am trying to build a query that will get all long_url.destination's matching a slug "test". One slug might point to multiple long_url.destination's(URL shuffling, GEO matching, etc...). So I need the slug to get all long_url.destination's with the same short_url.slug.
Before I was running another query to get the short_id from the slug, then running another query to select all rows in long_url that had a matching short_id.
I think it might be quicker if I use an inner join, but I am unsure how to properly set it up.
I want to get all destination columns in table long_url with only the slug data in short_url without having to run a separate query to get the short_id from the slug.
Table: short_url
Columns: short_id | slug | enabled | timestamp
example: 1 test 1 1323343922
Table: long_url
Columns: long_id | short_id | destination | geo | enabled | timestamp
example: 1 1 http://www.test.com US 1 132334922
example: 2 1 http://www.test.co.uk UK 1 132334922
I got this so far:
SELECT destination, geo FROM long_url INNER JOIN short_url
ON long_url.short_id = short_url.short_id WHERE enabled = 1;
function get_long_urls($slug) {
$query = "SELECT....";
$stmt = $db->prepare($query);
$stmt->execute(array(':slug' => $slug));
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
return (array) $results:
}
example $results = array(
'http://www.test.com' => 'US',
'http://www.test.co.uk' => 'UK',
);
Thanks for any help.
select long_url.destination
, long_url.geo
from long_url
inner
join short_url
on long_url.short_id = short_url.short_id
where short_url.slug = :slug
and long_url.enabled = 1
You don't need to qualify all column names like I did, because in this particular query there wasn't any ambiguity. All I really did is add a bound parameter placeholder.
SELECT destination, geo FROM long_url LEFT JOIN short_url
ON (long_url.short_id = short_url.short_id) WHERE enabled = 1
I have the following 3 tables:
(PK = Primary Key, FK = Foreign Key)
Files Table
File ID (PK) File Name ...
------------ ---------
1 a.jpg ...
2 b.png ...
3 c.jpg ...
. .
. .
. .
Tags Table
Tag ID (PK) Tag Name ...
----------- ----------
1 Melbourne ...
2 April ...
3 2010 ...
. .
. .
. .
Files_Tags Table
File ID (FK) Tag ID (FK)
------------ -----------
1 1
1 5
1 7
2 2
2 4
3 3
. .
. .
. .
In PHP, I want to get a list of all tags along with the number of times the tag appears (i.e. the number of files that have this tag).
Is that possible to do with one MySQL query ?
Try GROUP BY on your tag id. Use a LEFT JOIN to include tags that exist in the tags table but aren't ever used.
SELECT
Tag_Name,
COUNT(Files_Tags.Tag_ID) AS cnt
FROM Tags
LEFT JOIN Files_Tags
ON Tags.Tag_ID = Files_Tags.Tag_ID
GROUP BY Tags.Tag_ID
Result:
Melbourne 1
April 1
2010 1
... ...
You may also want to add an ORDER BY Tag_Name or an ORDER BY COUNT(*) if you want the rows returned in sorted order.
Daniel Vassello also submitted an answer but deleted it. However his answer is quite easy to adapt to meet your new requirements. Here is his solution, modified to use a LEFT JOIN instead of an INNER JOIN:
SELECT t.tag_id,
t.tag_name,
IFNULL(d.tag_count, 0) AS tag_count
FROM tags t
LEFT JOIN
(
SELECT tag_id, COUNT(*) tag_count
FROM files_tags
GROUP BY tag_id
) d ON d.tag_id = t.tag_id;
You shouldn't use too much of GROUP BY, ORDER BY and * JOIN as those query are very heavy and it's not something you should base your code on.
If I was you, I would do multiple simple SELECT query and group the stuff together using PHP algorithms. This way, you're DB won't be hit by very slow query.
So basically, in your specific question I would have more than 1 query.
I would start by doing a
SELECT * FROM "tags_table".
In php, I would created a foreach loop that would count appearance of every tag in your "files_tags" table:
SELECT FILE_ID COUNT(*) FROM TAGS_TABLE WHERE TAG_ID = 'tag_uid'
It's mostly pseudo-code so I wouldn't expect those query to work but you get the general idea.