I am creating a bulletin board application. Each bulletin can be liked or disliked by users of the site.To keep track of the likes and dislikes I have created the following database table
id user_id bulletin_id like_dislike
1 1 1 1
2 1 2 0
3 3 1 1
4 2 1 0
In the like_dislike column 1 means 'Like It', 0 means 'Don't like it'
I know how to ask.
- How many times was bulletin 1 liked (2)
- How many times was bulletin 1 disliked (1)
But How do I do a query to ask those two questions at the same time? That is, how many times was bulletin 1 liked and disliked
liked disliked
2 1
I have tried the query
SELECT count(like_dislike) AS likes, count(like_dislike) AS dislikes FROM bulletins_ld
where bulletins_id = 1 AND likes = 1 AND dislikes = 0
but all I get is two twice which is not surprising.
The only solution I can think of is having a separate like and dislike column
You can do this with an aggregate query, using opposing conditions on the single like_dislike column (I am assuming below that a '1' in that column means 'liked').
SELECT bulletin_id,
SUM(CASE WHEN like_dislike = 1 THEN 1 ELSE 0 END) AS likes,
SUM(CASE WHEN like_dislike = 0 THEN 1 ELSE 0 END) AS dislikes
FROM bulletins_ld
GROUP BY bulletin_id
Update: As per the discussion in the comments below, the like/dislike column could be normalized into its own table, like so (example deliberately silly...):
CREATE TABLE how_user_feels(
feeling_id INT,
feeling_desc VARCHAR(20)
)
INSERT INTO how_user_feels(feeling_id, feeling_desc) VALUES
(0, 'Undecided'),
(1, 'Likes It'),
(2, 'Could Do Without It')
The Likes_Dislikes column in the Bulletin table is then replaced by the foreign key feeling_id, with a default to 0. Let's say that you then enter a record in this table when a user first views a bulletin, making them "Undecided" by default, and update that record when they vote on the bulletin. You could query the results like so:
SELECT bulletin_id,
SUM(CASE WHEN feelings_id = 1 THEN 1 ELSE 0 END) AS likes,
SUM(CASE WHEN feelings_id = 2 THEN 1 ELSE 0 END) AS dislikes,
SUM(CASE WHEN feelings_id = 0 THEN 1 ELSE 0 END) AS doesnt_seem_to_care
FROM bulletins_ld b
INNER JOIN how_user_feels h ON b.feeling_id = h.feeling_id
GROUP BY bulletin_id
Keep in mind, this is just one approach, and may not be useful in your case. But if you ever decided to change or expand the model by which a user expresses their feelings for a bulletin, say to a five-star rating system, you could do that without changing the database schema - just alter the records in the how_user_feels table, and the associated queries.
For the record, there is another way to obtain the same result set, whether it is faster or slower depends on the platform. This uses a scalar subquery instead of an aggregate on the outer query. The idea is, this is supposed to be easier if you think in terms of sets. Or in terms of Dimension-Facts.
First we have to straighten out your names. Let's call your "table" bulletin_like and the main bulletin table bulletin (bulletin_id is a very silly name for either of them, that is more of a column name). And just call the boolean column like (if it is 1, like is true; if it is 0, like is false; that's what boolean means). Use the singular form for names. SELECT name AS bulletin,
(SELECT COUNT(like)
FROM bulletin_like bl
WHERE bl.bulletin_id = b.bulletin_id
AND like = 1
) AS like,
(SELECT COUNT(like)
FROM bulletin_like bl
WHERE bl.bulletin_id = b.bulletin_id
AND like = 0
) AS dislike
FROM bulletin b
You asked for the Normalisation tag,. That bulletin_like "table" is not Normalised. Get rid of theIdiot column, it serves no purpose other than a redundant column and an additional index. The PK is (bulletin_id, user_id).
Unless you want users to post multiple likes and dislikes per poster per bulletin.
This query worked for me:
SELECT bulletin_id,
sum(like_dislike) AS likes,
sum((1-like_dislike)) AS dislikes
FROM bulletins_ld
GROUP BY (bulletin_id);
This assumes that likedislike = 1 means "LIKE IT" and likedislike = 0 means "DOES NOT LIKE IT".
ID LIKES DISLIKES
1 2 1
2 0 1
Related
Hello I have 3 Tables but i need only help with 2 Tables.
The first Table is champions. In this Table are over 100 names.
The second Table is champion_names there are nicknames for the first Table.
The third Table is champion_names_vote. There are the votes for the nicknames. Thumb up if I like any nickname or thumb down.
On my website I have a site where I can see a list full of Names (Table 1). There are 2 columns. In the first is the normal name (Table 1) in the second the nickname (Table 2). Now I want to show the best nickname in column 2. Actually it's random but I only want to show the best nickname.
I can show all Names that's not the problem. But if I want to show only the best nicknames I don't know how.
Table 2: id(AI), champ_id(this is the id for Table 1), sender_id, name
Table 3: id(AI), userid, name_id(Table 2 ID), like_dislike
like_dislike = 1 is like, -1 is dislike and 0 is nothing.
Example:
Table 2: 50, 2, 4, Test
Table 3: 1, 3, 50, 1,
I liked the name of Table 2. So the name_id in Table 3 is the id of Table 2
So how can I do this with JOIN?
Can you help me please.
something like this should do the trick.
Idea is to Analyse your votes per nickname first. Then select the Maximum vote-score by HAVING:
SELECT voteResult.name, vote as winnerVoteScore
FROM(
select a.name_id, b.name,
sum(IF(a.like_dislike = 1, 1, IF(a.like_dislike = 2, -1, 0 ))) as vote
FROM champion_names_vote as a JOIN champion_names as b
ON a.name_id = b.ID
GROUP BY 1
) as voteResult
GROUP BY 1
HAVING vote = max(vote)
I have a table and would like to calculate values. My table:
Table votes
vote type
1 1
-1 1
1 0
-1 0
1 0
-1 1
1 0
Vote: -1 - Down; +1 - Up;
I would like to get something like this:
Count votes up type 1: 1
Count votes down type 1: 2
Votes up type 0: 3
Votes down type 0: 1
Sum votes type 1: 1+(-1)+(-1) = -1
Sum votes type 0: 1+(-1)+1+1 = 2
Is it possible to get results from mysql using single query?
Thanks!
I guess your problem is how to distinct up and down votes here.
You want to group by type (which sounds like a vote/poll ID) and then just pick up and down votes. It can be done using CASE or IF in combination with COUNT or SUM.
COUNT does not count NULL values so I suggest to wrap two COUNTs in IFs.
SELECT
type,
COUNT(IF(vote = -1, TRUE, NULL)) AS down,
COUNT(IF(vote = 1, TRUE, NULL)) AS up
FROM votes
GROUP BY type
SQL Fiddle: http://sqlfiddle.com/#!2/36008/1/0
you can write
select count(*) from table where type = 1 and vote = -1
select count(*) from table where type = 1 and vote = 1
the above will give you the result count which you want .
This will work
select sum(vote),type from table group by type;
I have a website with different articles. The database structure is like this:
ArticleId | ArticleLocation | ArticleCategory | ArticleTopic
The actual text strings for the columns are in another table so all the columns are populated with numbers (integers)
I want to find related posts, meaning if a user reads an article with ArticleLocation = 1, ArticleCategory= 3 and ArticleTopic = 2, then I want to find top 5 of articles sharing the most of the same column values.
Any ideas of how to do this?
Thanks in advance
Assuming the table is named ArticleTable, try the following:
SELECT *, (
CASE ArticleLocation WHEN :loc THEN 1 ELSE 0 END +
CASE ArticleTopic WHEN :topic THEN 1 ELSE 0 END +
CASE ArticleCategory WHEN :cat THEN 1 ELSE 0 END) AS Relevance
FROM ArticleTable
ORDER BY Relevance DESC
LIMIT 5
with :loc, :topic and :cat set to the relevant values.
I do not know how to classify this question. Vaguely, its about using calculated value in the WHERE clause of a mysql query performed using a php script.
Here's the scenario -
I've a mysql table with structure like this: table_id[int], item_id[int], item_rating[int]
Now the item_rating column can have either a "1" or a "0" value in it. The table_id column is set to auto_increment and the item_id column can have duplicate values also.
So a typical table will look like below -
table_id item_id item_rating
1 item1 1
2 item5 0
3 item1 1
4 item1 1
5 item5 1
6 item1 0
What i intend to do i for each item_id, i count the number of item_rating = 1 and item_rating = 0 and then take the difference of item_rating values to get the final rating for that item (final_item_rating = item_rating(with value=1) - item_rating(with value=0) ).
Now the issue:
I have a php script that takes values from these tables, and then displays the item details ordered on the "final_item_rating" value - something like:
select * from table_name order by final_item_rating desc
only problem is, since this final_item_rating is not a column in itself, and is actually based on run time value of the query, how do i build a query?
hope i have the question clear :)
This query may help you:
SELECT sum(item_rating) AS SumRatings
FROM table_name
WHERE item_rating=1
GROUP BY item_id
ORDER BY SumRatings;
This query would sum the ratings, and order the result with the highest rating on top:
select item_id
, sum(case when item_rating = 1 then 1 else -1 end) as rating
from YourTable
group by
item_id
order by
sum(case when item_rating = 1 then 1 else -1 end) desc
There's a query where I want to get the:
Username of the user attached to the current opportunity record
Sales Stage associated with each opportunity record
Dollar amount associated with opportunity record
I want to:
Take the current IF STATEMENT result, and collapse it
Current Query:
$sql = "SELECT u.user_name as USER,
if(o.sales_stage='Prospecting', o.amount, '') as PROSPECTING,
if(o.sales_stage='Needs Analysis', o.amount, '') as NEEDS_ANALYSIS,
if(o.sales_stage='Closed Won', o.amount, '') as CLOSED_WON
FROM opportunities o,
users u
WHERE o.assigned_user_id = u.id
GROUP BY u.user_name ";
Current Result:
USER PROSPECTING NEEDS_ANALYSIS CLOSED_WON
---------------------------------------------
chris 10000 0 0
chris 0 15000 0
chris 0 0 10000
sara 5000 0 0
sara 0 0 10000
What I'd like to do is collapse the results where I only get 1 user, and their respective amounts per SalesStage
USER PROSPECTING NEEDS_ANALYSIS CLOSED_WON
---------------------------------------------
chris 10000 15000 10000
sara 5000 0 10000
The "collapsing" is generally referred to as "pivoting", because you're converting row into columnar data. Use:
SELECT u.user_name as USER,
MAX(CASE WHEN o.sales_stage = 'Prospecting' THEN o.amount END) AS PROSPECTING,
MAX(CASE WHEN o.sales_stage = 'Needs Analysis' THEN o.amount END) AS NEEDS_ANALYSIS,
MAX(CASE WHEN o.sales_stage = 'Closed Won' THEN o.amount END) AS CLOSED_WON
FROM OPPORTUNITIES o
JOIN USERS u ON u.id = o.assigned_user_id
GROUP BY u.user_name
you need to add aggregating functions to your $ amounts... ie:
SELECT
u.user_name as USER,
if(o.sales_stage='Prospecting', SUM(o.amount), '') as PROSPECTING,
if(o.sales_stage='Needs Analysis', SUM(o.amount), '') as NEEDS_ANALYSIS,
if(o.sales_stage='Closed Won', SUM(o.amount), '') as CLOSED_WON
FROM
opportunities o,
users u
WHERE
o.assigned_user_id = u.id
GROUP BY
u.user_name
EDIT
Now that I think about it, you'll probably still get separate rows because the sales stages may differ among records for the same user. Have you considered using a routine to do the calculations for each sales stage to collapse your results down to one row per user?
You could wrap the current query in another query, like this:
select subquery.user,
sum(subquery.propspecting),
sum(subquery.needs_analysis),
sum(subquery.closed_won)
from (*...your query goes here*) subquery
group by subquery.user