voting system, relational db, sql query - mysql

I want to design voting system with two tables.
First table contains candidates' index and name.
The other one contains index, voter and candidate's index whom the voter support.
One voter can support multiple candidates.
I want a sql query that shows candidates' name with number of its supporters.
So the result looks like
John 12, Bob 8, David 3...
SELECT `name`, COUNT(table2.voter) AS `count`
FROM `table1`
LEFT JOIN `table2`
ON table1.idx = table2.support
ORDER BY COUNT(table2.voter) DESC;
The above query gave only one row with total number of voter.
Can anyone give me any hints?

SELECT `name`, COUNT(table2.voter) AS `count`
FROM `table1`
LEFT JOIN `table2` ON table1.idx = table2.support
GROUP BY `name`
ORDER BY COUNT(table2.voter) DESC;
You were missing a group by and hence getting only the first result.

You need to GROUP BY the non-aggregate column (name), otherwise the query will default to one group; the entire result set; and pick an arbitrary name:
SELECT `name`, COUNT(table2.voter) AS `count`
FROM `table1`
LEFT JOIN `table2`
ON table1.idx = table2.support
GROUP BY `name`
ORDER BY count DESC;
You can use column aliases in an ORDER BY, so I have update this also

Related

MySql group by columns and assign them a unique group_id

Basically, there is an abstract task on removing duplicates from db, which linked by it's id with several other tables...
I need to assign for each repeating row in table a unique group_id as max(id) of existing row. Please help
My question in picture
https://i.stack.imgur.com/CVYG1.png
You can group the entities (to find the group ids) at first and then update the group_id of entities.
Example:
UPDATE `t`,
(SELECT `name`, `surname`, MAX(`id`) AS `group_id` FROM `t`
WHERE 1 GROUP BY CONCAT(`name`, `surname`)) AS `t1`
SET `t`.`group_id` = `t1`.`group_id`
WHERE `t`.`name` = `t1`.`name` AND `t`.`surname` = `t1`.`surname`

Condense 3 SELECTS down to one table

The three statement (a little contrived for simplicity) are:
SELECT `user_id` WHERE `movie_id` = 1
SELECT `user_id` WHERE `movie_id` = 2
SELECT `user_id` WHERE `movie_id` = 3
What I would like to do is twofold:
Join the three of the above into a single table of ID's that match all three of the movie_id
Make all of this one query to reduce MySQL reads to speed up performance
I am sure there is a simple way to do it, but I am not the strongest SQL user, so thanks for all of the help!
Select count(distinct movie_ID), user_ID
from table where move_Id in (1,2,3)
group by user_ID
Having count(distinct Movie_ID) = 3
What this does is obtain a distinct count of movies per user_ID. it then limits the results to only those users having a count of 3.
The simplest way to me seems:
SELECT user_id FROM table_name WHERE movie_id IN (1,2,3);
Alternatively you could use the UNION method suggested by xQbert, but I would run an EXPLAIN query to see what SQL statement provided the most efficient result.

MySQL: Sort items by category count

I currently use the following query which works perfectly:
SELECT * FROM `items` WHERE `id` IN
(SELECT `item_id` FROM `categories_items`
WHERE `category_id` IN (1, 2) GROUP BY `item_id`
HAVING COUNT(`item_id`) = 2);
It selects all the items that are in all the selected (checkboxes) categories.
The problem is that most items are in many categories and a few items are only in two categories and when I only check those two, I still get a list of hundreds of items, making it nearly impossible to find the items that are in few categories.
My first idea was to add an ORDER BY "number_of_total_categories_that_the_selected_item_is_in" ASC somewhere in the query, but since I even got help with the current one and there would probably be a lot of calculations/subqueries for it to work, I thought of an extra column in the items table that would hold the number of categories it's in!
Is it possible to add an effective ORDER BY clause to the query and if so, what would it look like?
Do you have any other ideas? Would "an inverted" foreign key solution work here? Not a chance, right? :p
If not, all I can think of is to manually update a category_count column in items whenever it's needed.
Edit: Table field that holds row count from another table looks interesting, but I have no idea whether it would work in MySQL.
You want to use join rather than in. The following query filters for items that only have the two categories you want. It also counts the total number of categories, which can be used for the order by:
SELECT i.*
FROM `items` i JOIN
(SELECT `item_id`, COUNT(*) as cnt
FROM `categories_items`
WHERE `category_id` IN (1, 2)
GROUP BY `item_id`
HAVING COUNT(DISTINCT CASE WHEN category_id IN (1, 2) THEN category_id END) = 2
) c
ON i.id = c.item_id
ORDER BY cnt ASC;
EDIT:
If you want to count all the categories, then get rid of the where. It is not really doing anything:
SELECT i.*
FROM `items` i JOIN
(SELECT `item_id`, COUNT(*) as cnt
FROM `categories_items`
GROUP BY `item_id`
HAVING COUNT(DISTINCT CASE WHEN category_id IN (1, 2) THEN category_id END) = 2
) c
ON i.id = c.item_id
ORDER BY cnt ASC;

MySQL -- Making it all one query

I am trying to see if I can accomplish the following situation in one query:
I have a table with multiple columns, however, only two are important: version and groupId.
Many rows can share the same groupId value, the version column is a number that needs
to be sorted.
Given two groupId values, A and B, I would like to return two rows in the end. I want to find the most recent version number for each group A and B.
Thanks for your help. Sorry if this is fairly obvious, but I was having difficulty
Something like this?
SELECT groupId, MAX(version) max_version
FROM YourTable
WHERE groupId IN ('A', 'B')
GROUP BY groupId;
You didn't specify any data types so I assumed that groupId could actually take character values like 'A'. Just change this to suit your needs. The basic idea is that you GROUP BY your groupId after filtering out only those values which interest you. Then you SELECT the MAX(version) for each of those values.
Try below
select p.id,p.groupid,p.version from tablename p
left join
(
select max(id) id1 from tablename
group by groupId
order by max(id) desc
) t on t.id1 = p.id
Assuming you have a primary key column id in table
I assume version is an integer
SELECT MAX(version), `group` FROM table WHERE `group` IN (A, B) GROUP BY `group`

How do I write this kind of query (returning the latest avaiable data for each row)

I have a table defined like this:
CREATE TABLE mytable (id INT NOT NULL AUTO_INCREMENT, PRIMARY KEY(id),
user_id INT REFERENCES user(id) ON UPDATE CASCASE ON DELETE RESTRICT,
amount REAL NOT NULL CHECK (amount > 0),
record_date DATE NOT NULL
);
CREATE UNIQUE INDEX idxu_mybl_key ON mytable (user_id, amount, record_date);
I want to write a query that will have two columns:
user_id
amount
There should be only ONE entry in the returned result set for a given user. Furthermore, the amount figure returned should be the last recoreded amount for the user (i.e. MAX(record_date).
The complication arises because weights are recorded on different dates for different users, so there is no single LAST record_date for all users.
How may I write (preferably an ANSI SQL) query to return the columns mentioned previously, but ensuring that its only the amount for the last recorded amount for the user that is returned?
As an aside, it is probably a good idea to return the 'record_date' column as well in the query, so that it is eas(ier) to verify that the query is working as required.
I am using MySQL as my backend db, but ideally the query should be db agnostic (i.e. ANSI SQL) if possible.
First you need the last record_date for each user:
select user_id, max(record_date) as last_record_date
from mytable
group by user_id
Now, you can join previous query with mytable itself to get amount for this record_date:
select
t1.user_id, last_record_date, amount
from
mytable t1
inner join
( select user_id, max(record_date) as last_record_date
from mytable
group by user_id
) t2
on t1.user_id = t2.user_id
and t1.record_date = t2.last_record_date
A problem appears becuase a user can have several rows for same last_record_date (with different amounts). Then you should get one of them, sample (getting the max of the different amounts):
select
t1.user_id, t1.record_date as last_record_date, max(t1.amount)
from
mytable t1
inner join
( select user_id, max(record_date) as last_record_date
from mytable
group by user_id
) t2
on t1.user_id = t2.user_id
and t1.record_date = t2.last_record_date
group by t1.user_id, t1.record_date
I do not now about MySQL but in general SQL you need a sub-query for that. You must join the query that calculates the greatest record_date with the original one that calculates the corresponding amount. Roughly like this:
SELECT B.*
FROM
(select user_id, max(record_date) max_date from mytable group by user_id) A
join
mytable B
on A.user_id = B.user_id and A.max_date = B.record_date
SELECT datatable.* FROM
mytable AS datatable
INNER JOIN (
SELECT user_id,max(record_date) AS max_record_date FROM mytable GROUP BS user_id
) AS selectortable ON
selectortable.user_id=datatable.user_id
AND
selectortable.max_record_date=datatable.record_date
in some SQLs you might need
SELECT MAX(user_id), ...
in the selectortable view instead of simply SELECT user_id,...
The definition of maximum: there is no larger(or: "more recent") value than this one. This naturally leads to a NOT EXISTS query, which should be available in any DBMS.
SELECT user_id, amount
FROM mytable mt
WHERE mt.user_id = $user
AND NOT EXISTS ( SELECT *
FROM mytable nx
WHERE nx.user_id = mt.user_id
AND nx.record_date > mt.record_date
)
;
BTW: your table definition allows more than one record to exist for a given {id,date}, but with different amounts. This query will return them all.