How to obtain a relevance score in a table - mysql

I have a mysql table that has typetags for games, like
table game_typetags:
name typetag
--------- --------
game#1 sports
game#1 soccer
game#2 race
game#2 sports
and another table games like
name playcount
--------- ---------
game#1 10
game#2 8
And I want to list relevant games (score them with number of common typetags) and sort with playcount.
So I'm trying to get such an array in PHP:
function getSimilarGames($typetags_array)
{
$pdo->query("SELECT * FROM games COUNT (*) AS ???? ORDER BY games.playcount DESC");
}
So that when I call this function with $typetags = [race,sports] , it should return
name score
____ _____
game#2 2
game#1 1
What query should I use for this?
Thanks !

I do not agree with your database design, you should try to normalize it a bit but here is code that would work on your design:
SELECT games.name, COUNT(1) score
FROM games
INNER JOIN game_typetags ON game_typetags.name = games.name
WHERE game_typetags.typetag IN ('race', 'sports')
GROUP BY games.name
ORDER BY score DESC
I suggest a design like so;
game
-------
id integer pk
name varchar
typetag
-------
id integer pk
tagname varchar
game_typetags
-------
game_id int pk
typetag_id int pk
With this design you join together your tables using ID's instead of using strings everywhere.
A query to get the data you want from this could look like this, note that you could even use the tag-ids to make it tad faster (working on primary key in the database instead of string comparison in the where clause)
SELECT game.id, game.name, game.playcount, COUNT(1) score
FROM game
INNER JOIN game_typetags gtt ON gtt.game_id = game.id
INNER JOIN typetag tt ON tt.id = gtt.typetag_id
WHERE tt.typetag IN ('race', 'sports')
GROUP BY game.id
ORDER BY score DESC

And I figured out a solution to this problem, as follows:
SELECT name, SUM(matches) as relevance_score FROM
(
SELECT
game_typetags.*,
(typetag = 'race' )
+ (typetag = 'sports' )
AS matches
FROM game_typetags HAVING matches>0
ORDER BY matches DESC
) a GROUP BY name ;

Related

The query is not giving a desired output which I want

Query with OR which outputs wrong
SELECT DISTINCT
sm___employees.id,
sm___employees.employee_code,
sm___employees.leaving_date,
sm___employees.name_of_employee,
sm___employees.position,
sm___employees.rating,
sm___employees.entry_date
FROM
sm___employees
JOIN
sm___employee_skills
ON
sm___employees.id=sm___employee_skills.employee_id
WHERE
((sm___employee_skills.skill_id=1 AND sm___employee_skills.ans LIKE '%MBA%')
**OR**
(sm___employee_skills.skill_id=5 AND sm___employee_skills.ans IN (3)))
AND
sm___employees.rating IN (1)
ORDER BY
sm___employee_skills.date DESC
But I want it by And
SELECT DISTINCT
sm___employees.id,
sm___employees.employee_code,
sm___employees.leaving_date,
sm___employees.name_of_employee,
sm___employees.position,
sm___employees.rating,
sm___employees.entry_date
FROM
sm___employees
JOIN
sm___employee_skills
ON
sm___employees.id=sm___employee_skills.employee_id
WHERE
((sm___employee_skills.skill_id=1 AND sm___employee_skills.ans LIKE '%MBA%')
**AND**
(sm___employee_skills.skill_id=5 AND sm___employee_skills.ans IN (3)))
AND
sm___employees.rating IN (1)
ORDER BY
sm___employee_skills.date DESC
When am using first query with OR of MBA or 3, It gives me result for both which is correct as per OR operation
I want only those records which are having MBA AND 3 which gives me blank records when there are records available with this comparison
So please help me to resolve this.
Thank you in advance
To start with: DISTINCT often indicates a badly written query. This is the case here. You are joining records only to dismiss them later. If you want employee records, then select from the employee table. If you have criteria on the skills table check this in the WHERE clause. Don't join.
Then the WHERE clause looks at one row at a time. So neither skill_id = ... AND skill_id = ... nor skill_id = ... OR skill_id = ... can work for you. You must look up the skills table twice:
SELECT
id,
employee_code,
leaving_date,
name_of_employee,
position,
rating,
entry_date
FROM sm___employees
WHERE rating IN (1)
AND id IN
(
SELECT employee_id
FROM sm___employee_skills
WHERE skill_id = 1 AND ans LIKE '%MBA%'
)
AND id IN
(
SELECT employee_id
FROM sm___employee_skills
WHERE skill_id = 5 AND ans IN (3)
);
And here is a way to look up skills just once:
SELECT
id,
employee_code,
leaving_date,
name_of_employee,
position,
rating,
entry_date
FROM sm___employees
WHERE rating IN (1)
AND id IN
(
SELECT employee_id
FROM sm___employee_skills
WHERE (skill_id = 1 AND ans LIKE '%MBA%')
OR (skill_id = 5 AND ans IN (3))
GROUP BY employee_id
HAVING COUNT(DISTINCT skill_id) = 2 -- both skills
);
It seems strange though that you consider ans to be a string in one place (ans LIKE '%MBA%') and a number in another (ans IN (3)).
UPDATE: If you want to sort by skill date, you should consider by which skill's date. For this to happen, you would join, but not join the skills table, but the skills aggregate result. E.g.:
SELECT
e.id,
e.employee_code,
e.leaving_date,
e.name_of_employee,
e.position,
e.rating,
e.entry_date
FROM sm___employees e
JOIN
(
SELECT employee_id, MAX(date) AS max_date
FROM sm___employee_skills
WHERE (skill_id = 1 AND ans LIKE '%MBA%')
OR (skill_id = 5 AND ans = 3)
GROUP BY employee_id
HAVING COUNT(DISTINCT skill_id) = 2 -- both skills
) s ON s.employee_id = e.id
WHERE e.rating = 1
ORDER BY s.max_date;
Please try this :
SELECT DISTINCT
sm1.id,
sm1.employee_code,
sm1.leaving_date,
sm1.name_of_employee,
sm1.position,
sm1.rating,
sm1.entry_date
FROM sm___employees sm1
LEFT JOIN sm___employee_skills sm2 ON sm1.id = sm2.employee_id
WHERE ((sm2.skill_id=1 AND sm2.ans LIKE '%MBA%')
AND (sm2.skill_id=1 AND sm2.ans=3))
AND sm1.rating IN (1)
ORDER BY sm2.date DESC;

Join 2 tables and display by id,priority & number

Example tables and desired result:
The result table shown below is the output I actually want.
tried the following query with pivot:
with pivot_data AS
(
select client_id
,ph_type
,Ph_number
from client_table
inner join phone_table
on client_table.phone_id = phone_table.ph_id
)
select *
from pivot_data
pivot (sum(ph_number)
for ph_type in ('c','w','h')
);
Result I got:
Any help would be appreciated.
Answers in sql server would be great but oracle & mysql is also welcome if they can point me in the right direction. :)
Thanks in advance.
Oracle Query:
SELECT *
FROM (
SELECT client_id, priority, phone_number, phone_type
FROM client_table c
LEFT OUTER JOIN
phone_table p
ON ( c.phone_id = p.phone_id )
)
PIVOT ( MAX( phone_type ) AS phonetype, MAX( phone_number ) AS phonenumber
FOR priority IN ( 1 AS Prio1, 2 AS Prio2, 3 AS Prio3 ) );
Output:
CLIENT_ID PRIO1_PHONETYPE PRIO1_PHONENUMBER PRIO2_PHONETYPE PRIO2_PHONENUMBER PRIO3_PHONETYPE PRIO3_PHONENUMBER
---------- --------------- ----------------- --------------- ----------------- --------------- -----------------
1 C 9999999999 H 5555555555 W 7777777777
You really need to do some reading on set based thinking and how what you are asking for will be very detrimental to your maintenance of the SSIS solution moving forwards.
All you need to do is export the data as is. If you absolutely have to have it all in the one CSV file, just join the two tables together and retain a normalised, scalable dataset that won't break if the number of priorities increases:
select c.client_id
,c.phone_id
,c.priority
,p.phone_type
,p.phone_number
from #Client c
join #Phone p
on c.phone_id = p.phone_id

SQL query creation

I have to do a query without using views that displays the automobile brand that has the maximum number of cars that have a single owner.
that's the table that I have
CAR(idcar, brand, name, year_p)
OWNER(idowner,name, b_date)
PROPERTY(idcar,idowner,date_b, date_s)
That's what I was writing..
SELECT Car.brand, COUNT(PROPERTY.idowner) as con
FROM PROPERTY JOIN CAR ON Car.idcar=Property.idcar
GROUP BY Car.brand
ORDER BY con ASC
LIMIT 1;
It's ok? what I can do to improve it?
That's how it should work
CAR
-----------------
1|FIAT|test1|2000
2|Alfa|test2|2011
3|FIAT|test3|2001
4|Alfa|test4|2006
5|Alfa|test5|2004
OWNER
-----------
1|John|1990
2|Jack|1980
3|Sam |1991
4|Rick|1980
5|Cris|1993
6|Holm|1992
PROPERTY
-------------
1|1|2000|2000
2|1|2001|2003
3|2|2000|2004
1|3|2002|2005
4|4|2004|2006
5|6|2003|2008
2|5|2001|2009
So the query should return Alfa because I've 3 people that have only one car and those 3 people have one alfa ( so 3 alfa). It can't be FIAT because I only have 2 people with only one car that is a FIAT(so 2 FIAT). The OWNER n1 is excluded because he has 2 car
I think you were on the right path with your query, it's only need a little modification...
First if you want to select brand with most owner than you should order them in descending order so the max will be at the top... And second you need to add WHERE clause which will exclude all owner that have more then one car...
It should look like this:
SELECT c.brand, COUNT(p.idowner) as con
FROM PROPERTY p
JOIN CAR c
ON c.idcar=p.idcar
WHERE p.idowner NOT IN (SELECT idowner
FROM (SELECT idowner, COUNT(idowner) as c
FROM PROPERTY
GROUP BY idowner) x
WHERE c > 1)
GROUP BY c.brand
ORDER BY con DESC
LIMIT 1;
Here is the SQL Fiddle to take a look how it's work...
GL!
This is too long for a comment.
Your query is not correct. It returns the brands that have the most properties for its cars. That may or may not have to do with the number of owners. I'm sure there's a relationship of some sort ("no properties" --> "no owners" and vice versa).
In any case, I don't know what you mean by "the maximum number of cars that have a single owner". You should edit your question with sample data and desired results. A SQL Fiddle would also be helpful.
--CTE to Get single owner
WITH CTEsingleowner(ct, idowner)
AS (SELECT Count(1) AS ct,
idowner
FROM property WITH(nolock)
GROUP BY idowner
HAVING Count(1) = 1)
-- Select brand with maximum number of single owner
-- with ties is used to resolve confilict more than one record set returned
SELECT TOP (1) WITH ties Count(1) AS cnt,
brand
FROM CTEsingleowner ct
JOIN property p WITH(nolock)
ON p.idowner = ct.idowner
JOIN car c WITH(nolock)
ON ( c.idcar = p.idcar )
GROUP BY brand
ORDER BY cnt DESC ;
--CTE to Get single owner
WITH CTEsingleowner(ct, idowner)
AS (SELECT Count(1) AS ct,idowner
FROM property WITH(nolock)
GROUP BY idowner
HAVING Count(1) = 1)
-- Select brand with maximum number of single owner
-- with ties is used to resolve conflict more than one rows returned
SELECT TOP (1) WITH ties Count(1) AS cnt, brand
FROM CTEsingleowner ct
JOIN property p WITH(nolock)
ON p.idowner = ct.idowner
JOIN car c WITH(nolock)
ON ( c.idcar = p.idcar )
GROUP BY brand
ORDER BY cnt DESC ;

Query on multiple tables?

I need to create a single query that reads from three tables in the same db.
Tables:
1. cm_players
player_id
card_no
club_id
surname
name
birthdate
2. cm_clubs
club_id
prefix
name
address
place
stadium_id
telephone
fax
email
web
contact_person
president
secretary
press_officer
status
image
3. cm_registrations
player_id
surname
name
birthdate
birthplace
club_id
date_from
date_to
amateur
status
So, I need to display
cm_players.name,
cm_players.surname,
cm_clubs.name,
cm_players.birthdate,
cm_players.card_no,
join cm_players with cm_clubs to get the name of the club, and check in cm_registrations if the user is active.....
I have tried something like:
SELECT cm_players.name, cm_players.surname, cm_clubs.name, cm_players.birthdate, cm_players.card_no, cm_registrations.amateur FROM cm_players
INNER JOIN cm_clubs on cm_players.club_id = cm_clubs.club_id, cm_registrations
WHERE cm_players.name LIKE '%$name%' AND cm_players.surname LIKE '%$surname%' AND cm_players.player_id = cm_registrations.player_id AND cm_registrations.amateur = 0 ORDER BY cm_players.player_id ASC
but no luck...
can anyone help me to build this query?
Here we are setting the table aliases, and get the column values by using appropriate alias names of that table and we are using inner joins on two tables that are clubs and registrations
SELECT cmp.name, cmp.surname, cmc.name, cmp.birthdate, cmp.card_no, cmr.amateur
FROM cm_players cmp
INNER JOIN cm_clubs cmc on cmp.club_id = cmc.club_id
INNER JOIN cm_registrations cmr on cmp.player_id = cmr.player_id
WHERE cmp.name LIKE '%$name%'
AND cmp.surname LIKE '%$surname%'
AND cmr.amateur = 0
ORDER BY cmp.player_id ASC
cm_registrations WHERE cm_players.name LIKE '%$name%' AND cm_players.surname LIKE '%$surname%' AND cm_players.player_id = cm_registrations.player_id AND cm_registrations.amateur = 0 ORDER BY cm_players.player_id ASC
error is after join u use , than cm_registeration change it and try to fix it.

SQL - Confusing Query

I have a 'photos' table:
photoID (INT), setID (INT)....
18900 , 234 , ...
18901 , 234 , ...
18902 , 234 , ...
18903 , 249 , ...
18904 , 249 , ...
18905 , 249 , ...
I also have a 'photoKeyword' table:
photoID (INT), keywordID (INT)
18900 , 12
18900 , 21
18901 , 17
18905 , 26
18905 , 10
As you can see from my examples above, photos 18902, 18903 and 18904 do NOT have any keywords in the photoKeyword table. This is exactly what I am trying to establish.
I am trying to produce a list of photoID's that don't have keywords but one setID at a time. So as you can see, photo 18902 doesn't have keywords and so does 18903 and 18904 but these three photos have two different setID's.
So running this query once, should only return photo 18902. I would then add keywords to this photo so it won't be a problem again. The next time I run the query it should return photo 18903 and 18904, the next set (setID:249) of photos that do not have keywords.
How is this possible? Is it possible just using SQL? I hope you can understand what I looking to achieve, I lost myself just writing about it!!
Any thoughts gratefully received...
try
SELECT X.photoID FROM photos X
INNER JOIN
(SELECT DISTINCT P.setID FROM
photos P
LEFT OUTER JOIN (SELECT K.photoID, COUNT(*) C FROM photoKeyword K GROUP BY K.photoID) KC ON KC.photoID = P.photoID
GROUP BY P.setID
HAVING SUM (KC.C) < 1) Y ON X.setID = Y.SetID
SELECT photoID
, setID
FROM photos
WHERE photoID NOT IN
( SELECT photoID
FROM photoKeyword
)
AND setID =
( SELECT setID
FROM photos
WHERE photoID NOT IN
( SELECT photoID
FROM photoKeyword
)
ORDER BY setID
LIMIT 1
)
This might be what you need which is quite simple... when you think about it... Ensure your keyword table has an index on the photo id
select
p.PhotoID,
p.SetID
from
Photos p
LEFT JOIN photoKeyword pkey
on p.PhotoID = pkey.PhotoID
where
pkey.PhotoID = null
By doing a LEFT join, we know it will always attempt to the second table. Then, if the second table has no matches, we know the ID trying to join on will be null... So, left join and return only those IDs where the answer is NULL in the second table.