Mysql query based on columns matches - php

I don’t know how to make this with mysql, I only know how to do basic queries, I want to show a list of result based on matches, Results will be shown based on the same value of the answers…answers may have a value from 1-10
+++++++++++++++TABLE++++++++++++
id | userName | answer1 | answer2 | answer3 | answer4….
10 Jhon 1 1 3 8
11 Anne 1 2 4 8
12 Mike 7 4 5 7
etc…
++++++++++++++++++++++++++++++++++++++++
If I send the values in the query I want that check the answers and show the result sorted my matches,
more matches first…no matches last
So if i send the results:
answer1=1 answer2=1 answer3=7 answer4=2...
the result should be(give back the id)
10 11 12

Your table design is not fine, you should separate it into users and questions table.
If you cannot change the table design you can solve you problem using this query:
select
id,
username,
if(answer1 = :an1, 1, 0) + if(answer2 = :an2, 1, 0) + if(answer3 = :an3, 1, 0) + if(answer4 = :an4, 1, 0) as total
from
table
order by total desc
UPDATE:
Better design for this problem:
Check the SQLFiddle: http://sqlfiddle.com/#!9/6c145/2 with a live demo.
Create Users Table
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL
);
Create Questions Table
CREATE TABLE questions (
id INT PRIMARY KEY,
correct_answer INT NOT NULL
);
Create User Answers Table
CREATE TABLE user_answers (
user_id INT,
question_id INT,
user_answer TINYINT,
PRIMARY KEY (user_id, question_id),
FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE NO ACTION ON UPDATE NO ACTION,
FOREIGN KEY (question_id) REFERENCES questions (id) ON DELETE NO ACTION ON UPDATE NO ACTION
);
Than to retrieve the data you can use the query:
SELECT
tmp.id,
tmp.username,
sum(tmp.is_correct) as total
FROM (
SELECT
users.id,
users.username,
IF (questions.correct_answer = user_answers.user_answer, 1, 0) as is_correct
FROM
users
INNER JOIN user_answers on users.id = user_answers.user_id
INNER JOIN questions on user_answers.question_id = questions.id
) tmp
GROUP BY tmp.id, tmp.username
ORDER BY total desc;

Related

PHP MySQL Group BY Having issue

Suppose I have a table like:
ID|Word |Reference
1 |Dog |1
1 |Fish |2
1 |Sheep|3
2 |Dog |4
2 |Fish |5
3 |Sheep|6
4 |Dog |7
I want to select all ID's that have the word Dog AND Sheep. So the result should be ID's: 1 and 2. I tried using this query:
SELECT ID FROM `Table` WHERE Word='Dog' OR Word='Fish' GROUP BY ID Having Word='Dog AND Word='Fish'
However, this AND in the Having clause makes me get 0 results. So, am I doing something wrong or is there another way to achieve wat I want based on MySQL query only (to optimize speed, since it has to search through many rows with the same setup as in the example above)
Basically the problem is the AND statement over multiple rows with the same ID.
UPDATE:
I need to get the reference for the ID's that where found. E.g. when the ID 1 and 2 are returned I need to know that ID 1 has reference 1 and 2. ID 2 has reference 3 and 4. Currently, I'm using this query:
SELECT ID FROM `Test` WHERE Word in ('Dog', 'Fish') GROUP BY ID HAVING count(DISTINCT Word) = 2;
Thanks
Here are two solutions that return the correct records, the first as individual records by ID and Reference, and the second with one record per ID and the Words and References as comma separated in columns.
Setup table and populate rows:
DROP TABLE IF EXISTS `list1`;
CREATE table `list1` (
id int(10),
Word varchar(10),
Reference int(10)
);
INSERT INTO `list1` (`ID`, `Word`, `Reference`)
VALUES
(1, 'Dog',1),
(1 ,'Fish',2),
(1 ,'Sheep',3),
(2 ,'Dog',4),
(2 ,'Sheep',5),
(3 ,'Sheep',6),
(4 ,'Dog',7);
Returns one row for each combination of ID and Word
SELECT
t.`ID`,
t.`Word`,
t.`Reference`
FROM `list1` as t
JOIN (
SELECT
t1.`ID` as `ref_id`
FROM `list1` AS t1
WHERE `Word` in ('Sheep','Dog')
GROUP BY t1.`ID`
HAVING count(DISTINCT t1.`Word`) = 2
) AS ts
ON t.`ID` = ts.`ref_id`
WHERE t.`Word` in ('Sheep','Dog')
ORDER BY t.`ID`,t.`Word`;
Results
ID | Word | Reference
1 | Dog | 1
1 | Sheep | 3
2 | Dog | 4
2 | Sheep | 5
Returns one row per ID, with a comma separated list of Words in one column, and a comma separated list of Reference in another.
SELECT
t.`ID`,
GROUP_CONCAT(t.`Word`) AS `Words`,
GROUP_CONCAT(t.`Reference`) AS `References`
FROM `list1` as t
JOIN (
SELECT
t1.`ID` as `ref_id`
FROM `list1` AS t1
WHERE `Word` in ('Sheep','Dog')
GROUP BY t1.`ID`
HAVING count(DISTINCT t1.`Word`) = 2
) AS ts
ON t.`ID` = ts.`ref_id`
WHERE t.`Word` in ('Sheep','Dog')
GROUP BY t.`ID`
ORDER BY t.`ID`,t.`Word`;
Results:
ID | Words | References
1 | Dog,Sheep | 1,3
2 | Dog,Sheep | 4,5
You need to join the table on itself. This way you can pick up where the id's are the same for instances where dog and sheep overlap.
Try this:
declare #t table (id int , Word varchar(10) )
insert into #t (ID, Word) values (1, 'Dog'),
(1 ,'Fish'),
(1 ,'Sheep'),
(2 ,'Dog'),
(2 ,'Sheep'),
(3 ,'Sheep'),
(4 ,'Dog')
select t.ID
from #t as t
join #t as t1 on t1.id = t.id
where t.word = 'Dog' and t1.word = 'Sheep'
Here's one way to do it by joining your table to itself.
SELECT t1.id FROM `Table` t1
INNER JOIN `Table` t2 ON t1.id = t2.id
WHERE t1.word='Dog' AND t2.word='Sheep';
The answer for my problem is solved using underneath query:
SELECT ID, GROUP_CONCAT(Reference) as ReferencesGrouped FROM `Test` WHERE Word in ('Dog', 'Fish') GROUP BY ID HAVING count(DISTINCT Word) = 2;
This will return me:
ID|ReferencesGrouped
1 |1,4
2 |4,5

How can i get the result from DB if one column have several keywrods?

Well, I've a table called keywords and there are 2 columns.
1) kid
2) keywords (keyword value)
and I've another table called contact_details where there a column called keyword. In this keyword column I'm inserting many keyword from keywordstable. So 2 tables is look like this...
Keywords table:
kid keyword
1 php
1 mysql
1 html
1 css
1 css3
1 wp
1 photoshop
1 3d
contact_details table:
cid name phone keyword
1 alex 123 php, mysql, hmtl
2 alex1 124 php, html, css3
3 alex2 125 wp, html, css
4 alex3 126 photoshop, 3d
5 alex4 127 html, 3d, php
6 alex5 128 mysql, wp, html
Now I've a search box which is searching people (name From contact_details table) by using keyword value. In search box, Search value could be few keywords. I mean it's could be php, mysql, html or could be php, 3d, photoshop.
So My question is : how can I write a Sql query to get the result ? I want to get all name which match the search keyword/s from contact_details table ?
Is there any field need to add in keywords table ? Can not get an IDEA :(
Select name from contact_details where keyword like '%<search keywords>%'
Like search keyword key php then you need pass php in query and will get list of all name which having keywords as php'
Select name from contact_details where keyword like '%php%'
hope this will resolve your issue.
Proper approach,
Make kid as primary key in Keyword table
Keywords table:
kid keyword
1 php
2 mysql
3 html
Remove keywords column from contact_details table.
contact_details table:
cid name phone
1 alex 123
2 alex1 124
3 alex2 125
Make one more table which having many to many relationship and you need insert the relationship here so that no need touch again keywords and contact_details table again.
keyword_contact_mapping
kcid kid_fk cid_fk
1 1 1
2 1 2
3 1 3
4 2 1
5 2 1
6 2 2
7 2 3
Sql query (Not tested you can also use alias)
select name from contact_details join keyword_contact_mapping on kid_fk =(select kid from Keywords where keyword='php')
Assuming you have the ID when the name is selected, you can use:
SELECT Keyword from Keywords_Table
WHERE ID = <ID>
USE AdventureWorksDW2008R2
GO
IF OBJECT_ID('Keywords') IS NOT NULL
BEGIN
DROP TABLE Keywords
END
IF OBJECT_ID('Contact_Details') IS NOT NULL
BEGIN
DROP TABLE Contact_Details
END
IF OBJECT_ID('Keyword_ContactDetails') IS NOT NULL
BEGIN
DROP TABLE Keyword_ContactDetails
END
/* automate id's with identity. Or, do you want to specify the the keyword id manually?
*/
CREATE TABLE Keywords(
Keywords_ID INT IDENTITY(1,1) NOT NULL
,Keyword NVARCHAR(100)
CONSTRAINT PK_Keywords PRIMARY KEY
(
Keywords_ID
)
)
/* You must plan each column data type by careful consideration
I am using the phone example here to demonstrate different business requirements
the lenghts and datatype may need to change for localization
*/
CREATE TABLE Contact_Details
(
Contact_Details_ID INT IDENTITY(1,1) NOT NULL
,First_Name VARCHAR(100)
,Last_Name VARCHAR(100)
,Phone VARCHAR(10)
,Phone_EXT VARCHAR(3)
,Phone_International NVARCHAR(15)
CONSTRAINT PK_Contact_Details PRIMARY KEY
(
Contact_Details_ID
)
)
CREATE TABLE Keyword_ContactDetails
(
Keyword_ID INT
,Contact_Details_ID INT
,DateTime_Created DATETIME
CONSTRAINT PK_KeywordContact PRIMARY KEY
(
Keyword_ID
,Contact_Details_ID
)
/*Enforce referential integrity,
prevents adding keywords that don't exist
prevents deleting a keyword if it is referenced
*/
FOREIGN KEY (Keyword_ID) REFERENCES Keywords(Keywords_ID),
FOREIGN KEY (Contact_Details_ID) REFERENCES Contact_Details(Contact_Details_ID)
)
/* Populate keywords
*/
INSERT INTO Keywords(Keyword) VALUES ('PHP')
INSERT INTO Keywords(Keyword) VALUES ('MYSQL')
INSERT INTO Keywords(Keyword) VALUES ('HTML')
INSERT INTO Keywords(Keyword) VALUES ('CSS')
/* Add contact details
*/
INSERT INTO Contact_Details(
First_Name
,Last_Name
,Phone
,Phone_EXT
,Phone_International)
VALUES(
'Abe'
,'Lincoln'
,'2129996677'
,'123'
,'na')
/* Assign PHP Keyword to Abe Lincoln
*/
DECLARE #keywordID int = 0
,#contactDetails int = 0
set #keywordID = (select Keywords_ID from Keywords where Keyword = 'PHP')
set #contactDetails = (select Contact_Details_ID from Contact_Details where Last_Name = 'Lincoln')
INSERT INTO Keyword_ContactDetails(
Keyword_ID
,Contact_Details_ID
,DateTime_Created)
VALUES(
#keywordID
,#contactDetails
,CURRENT_TIMESTAMP)
SELECT * FROM Contact_Details C
JOIN Keyword_ContactDetails KC
ON KC.Contact_Details_ID = C.Contact_Details_ID
JOIN Keywords K
ON K.Keywords_ID = KC.Keyword_ID

Only return a row if result is unique

I have a db table that looks like this:
product_tag_id | tag_id | product_id
1 1 1
2 1 2
3 3 1
I have some php that's running a query in a foreach loop:
$product_tags = [1,3];
foreach(product_tags as $tag) {
$q = $this->db->query("SELECT product.*, product_tags.* FROM product
INNER JOIN product_tags ON product_tags.product_id = product.product_id
WHERE product_tags.tag_id =" . $tag);
}
The first query using 1 as the $tag value pulls in product_id 1 and 2 and the second query using 3 as $tag pulls in product_id 1 again.
I only want the query to return a row if it hasn't returned the product_id before. I only want to see a result if the product_id is unique. Is there any way to do this?
I thought DISTINCT would achieve this for me but it doesn't seem to work.
Okay, you probably need a GROUP BY. I'm not really certain what the following actually means, you make it seem as though you have some sort of date column in the table.
"I only want the query to return a row if it hasn't returned the product_id before."
I'm going to ignore this and assume that your current data is as follows and you have no additional columns to worry about:
product_tag_id | tag_id | product_id
1 1 1
2 1 2
3 3 1
but you want to return this:
product_tag_id | tag_id | product_id
1 1 1
3 3 1
i.e. you want the minimum PRODUCT_ID per tag. So, you should select that:
SELECT pt.product_tag_id, pt.tag_id, min(p.product_id) as product_id
FROM product
INNER JOIN product_tags pt
ON pt.product_id = p.product_id
WHERE pt.tag_id = ?
GROUP BY pt.product_tag_id, pt.tag_id
Including DDL in your question will always get you more and better answers.
create table products (
product_id integer primary key,
product_name varchar(15) unique
);
create table product_tags (
product_tag_id integer primary key,
tag_id integer not null,
product_id integer not null,
unique (tag_id, product_id)
);
insert into products values (1, 'a'), (2, 'b'), (3, 'c');
insert into product_tags values (1, 1, 1), (2, 1, 2), (3, 3, 1);
I only want the query to return a row if it hasn't returned the product_id before.
A different (and more useful) way to say this is "I want one row per product_id." To which we have to ask, "Then which tag_id do you want when there's more than one tag_id per product?" Here, I return the minimum tag_id.
select products.product_id, products.product_name, tags.min_tag_id
from products
inner join
(select product_id, min(tag_id) min_tag_id
from product_tags
group by product_id) tags
on tags.product_id = products.product_id
where tags.min_tag_id = 1

PHP - MySQL Search database table return result with percentage match

On my site I allow the users to select what they like or have interest in. This is done using a pre-defined drop down menu, so every time the user logs into the site they get a list of users that have the same interest as them.
This is done by taking the logged in user's interests (store in db) and matching with other user on the site, using MySQL WHERE clause. But what I am having trouble is how to show percentage or score next to each users to show how close they match the logged in user interest.
For example:
user1 -- 60% match to your interest
user1 -- 30% match to your interest
user2 -- 20% match to your interest
Each user have 5 different interest, if all match than its 100% match.
A sample of table structure:
CREATE TABLE IF NOT EXISTS `helloworld` (
`id` int(9) NOT NULL AUTO_INCREMENT,
`like1` varchar(300) NOT NULL,
`like2` varchar(300) NOT NULL,
`like3` varchar(300) NOT NULL,
`name` varchar(300) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=7 ;
example query:
SELECT * FROM helloworld WHERE like1='football' AND like2='art'
I was thinking using COUNT function, but I am unsure? or should I be using sub queries?
EDIT : i am using PHP for server side language. user can NOT type their own likes, must use the pre defined list.
Here's how I do it. Assume that $like1, $like2 and $like3 are the values from your current user:
SELECT (IF(like1='$like1',1,0) + IF(like2='$like2',1,0) + IF(like3='$like3',1,0))/3*100 match_percent,
COUNT(id)
FROM helloworld
GROUP BY match_percent;
You have a many-to-mant relationship between user and likes. Your table violates 1NF - you have a "repeating group" of the like columns. Instead, have a separate association table to handle this:
create table user_likes (
user_id int(9) NOT NULL,
like_name varchar(300) NOT NULL
);
Now you can use simpler queries to get the count of matches - I'll leave that to you to work out :)
Hint: You could use a bit mask to help determine matches, assigning a predefined power of 2 number to each distinct like_name (help is a like_names table).
You'd better check that at PHP level. Considering each user's interests, you may get the score using count() over the result of array_intersect() to compare the visitor and other user interests (http://www.php.net/manual/en/function.array-intersect.php). If you allow 5 interests, that would be (5 * count(array_intersect({params})))%. For no match, 0%, for 4 matches, 80%.
First off, I think you need a different schema. The one you have will make your task very difficult, because it's not flexible enough. I recommend something like this:
CREATE TABLE `users` (
`id` INT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(300) NOT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `likes` (
`user` INT NOT NULL,
`interest` VARCHAR NOT NULL,
PRIMARY KEY (`user`,`interest`)
);
(Sorry, I don't remember how to set up a FK relationship in MySQL, but I'm sure you can figure that out easily enough.)
Then, to determine the number of 'likes' for each user:
SELECT COUNT(*)
FROM users
JOIN likes ON likes.user=users.id
WHERE users.name = 'bob';
Then to determine how many likes two users have in common:
SELECT COUNT(*)
FROM users AS u1
JOIN likes AS l1 ON (l1.user = u1.id)
JOIN likes AS l2 ON (l1.interest = l2.interest)
JOIN users AS u2 ON (l2.user = u2.id)
WHERE u1.name = 'bob'
AND u2.name = 'alice';
Then based on these three numbers, you can calculate your percentages as you wish--probably in your client code, but you could use sub-selects to do it all SQL-side if you want.
Example:
USERS:
id | name
----+-------
1 | bob
2 | alice
LIKES:
user | interest
------+----------
1 | fish
1 | baseball
2 | fish
2 | cooking
2 | baseball
Running the first query for bob and alice will show that bob has 2 interests, and that Alice has 3 interests. Then running the second query will show that they bob and alice together have 2 shared interests.
You can then show Bob that Alice shares 100% of his interests (2/2 = 100%), and you can show Alice that Bob shares 66% (2/3 = 66%) of her interests.
This will do it with your current schema:
select
t2.id,
t2.name
sum(
t1.like1 in (t2.like1, t2.like2, t2.like3, t2.like4, t2.like5) +
t1.like2 in (t2.like1, t2.like2, t2.like3, t2.like4, t2.like5) +
t1.like3 in (t2.like1, t2.like2, t2.like3, t2.like4, t2.like5) +
t1.like4 in (t2.like1, t2.like2, t2.like3, t2.like4, t2.like5) +
t1.like5 in (t2.like1, t2.like2, t2.like3, t2.like4, t2.like5)
) * 20 as percent_match
from helloworld t1
left join helloworld t2 on t1.id != t2.id
group by 1, 2
order by 3 desc;
This works because true in mysql is 1 - summing the truths will total the number of matches.

PHP & MySQL Update Database Problems

I'm trying to change my rating system which only used one table before but now I'm changing it to use multiple tables and I really dont no how to
update or insert a new rating into the database and was wondering how to do this using my MySQL tables structure?
Also how do I do this by adapting the new code to my current PHP code which I want to change which is listed below.
First let me explain what my tables do they hold the information when students rate there own teachers I listed what the tables will hold in the
examples below to give you a better understanding of what I'm trying to do. Students are only allowed to rate there teachers once.
I provided the two MySQL tables that should be updated which are listed below.
My MySQL tables
CREATE TABLE teachers_grades (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
grade_id INT UNSIGNED NOT NULL,
teachers_id INT UNSIGNED NOT NULL,
student_id INT UNSIGNED NOT NULL,
date_created DATETIME NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE grades (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
letter_grade VARCHAR(2) NOT NULL,
grade_points FLOAT UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY (id)
);
What the database will hold.
teachers_grades
id grade_id teachers_id student_id date_created
1 3 2 32 2010-01-23 04:24:51
2 1 32 3 2010-01-23 12:13:58
3 2 32 103 2010-01-23 12:24:45
grades
id letter_grade points
1 A+ 10
2 D 3
3 B 5
Here is the old PHP code.
// function to insert rating
function rate(){
$text = strip_tags($_GET['rating']);
$update = "update vote set counter = counter + 1, value = value + ".$_GET['rating']."";
$result = mysql_query($update);
if(mysql_affected_rows() == 0){
$insert = "insert into vote (counter,value) values ('1','".$_GET['rating']."')";
$result = mysql_query($insert);
}
}
Old table.
CREATE TABLE vote (
`counter` int(8) NOT NULL default '0',
`value` int(8) NOT NULL default '0'
);
first , do mysql_escape_string to the parametrs when inserting like :
mysql_real_escape_string($_GET['rating']);
second
you need to get all parameters (from GET or POST) and insert it to the db ,
the teacher_id ....
now i only see the rating.
Your old table was bit confusing as it seems like it only rates 1 teacher or teachers as a whole.
Now it seems like your new design process requires you to:
- store rating and averages of teachers
- track historical ratings from students
rating table should look something like
Table: rating
rating_id student_id teacher_id grade_id date_created
1 101 21 1 2010-01-23 04:24:51
2 102 21 1 2010-01-23 04:26:51
3 102 22 2 2010-01-23 04:28:51
4 103 24 1 2010-01-23 04:44:51
Your code usage:
$rating_value = $_GET['rating']; // Remember to sanitize your inputs
$student_id = $_GET['student_id'];
$teacher_id = $_GET['teacher_id'];
rate_a_teacher($teacher_id, $student_id, $rating_value);
Your method:
function rate_a_teacher($teacher_id, $student_id, $rating_value)
{
// Find the corrosponding to specified rating value
$grade_id = find_grade_id($rating_value); //TODO
$sql = "
INSERT INTO rating
('student_id', 'teacher_id', 'grade_id', 'date_created')
VALUE
($student_id, $teacher_id, $grade_id, NOW);
";
mysql_query($sql);
}
I skipped implementation for find_grade_id() for you to fill it in yourself.
The purpose of splitting your calcualted values to individual records is so that you can start do interesting reports,
like such:
Find average rating value of each teacher for the past 3 months:
SELECT teacher_id, (SUM(points)/COUNT(rating_id)) AS average_score
FROM rating
LEFT JOIN grades ON grades.id = rating.grade_id
WHERE date_created > SUBDATE(NOW(), INTERVAL 3 MONTH)
GROUP BY teacher_id

Categories