To begin with I have two tables:
feeds:(id,content_id,author) and
feeds_ratings:(id,feed_id (FK to feeds(id)),user_id,rating)
What I want to do is get the total rating difference for a specific author by a certain user.
To explain a bit more, let's say we have three rows in the feeds table, (1,3245,test),(2,3215,test),(3,3122,test) and tree rows on the feeds_ratings table, (1,1,12,like), (1,2,12,like),(1,3,12,dislike)
The input will be the user_id and the author and I want the output to be the difference between the total dislikes and likes by the input user, for the specific input author. (In this example it will be 1 because of the two likes and the one dislike.
How can that be implemented in a mysql query? I tried searching and some code of my own but I can't make it work, so any help is appreciated!
Something like this will work using SUM with CASE -- add 1 for likes and -1 for dislikes:
SELECT SUM(CASE WHEN fr.rating = 'like' THEN 1 ELSE -1 END) TotalLikes
FROM Feeds F
INNER JOIN Feeds_Ratings FR ON F.id = FR.feed_id
WHERE F.author = 'test'
AND FR.user_id = 12
SQL Fiddle Demo
Obviously replace author and user_id with the appropriate values -- these are just for your sample input.
Related
I'm hoping you guys can help me in SQL because i'm a newbie iN sql. My problem is, the code does not give the expected output and i do not know how to fix that eventough i've searched through books and online resources. I have 2 tables (customer & order_status). The task is:
(1) select c_id,lname,address,city,description where c_id > 3
(2) select c_id,lname,address,o_status,item_total,remarks and update description to 'black' where c_id =3
(3)if item_total > 2, select o_status,item_total. ELse select o_status,item_total,remarks,order_no and update remarks to 'set'
So, here's the code:
#drop procedure if exists usp_GetAnything;
delimiter //
create procedure usp_GetAnything()
begin
declare total int ;
select total = item_total
from order_status;
select c_id,lname,address,city,description
from customer
where c_id > 3;
select c.c_id,c.lname,c.address,o.o_status,o.item_total,o.remarks,c.description
from customer c,order_status o
where c.c_id=o.c_id;
update customer
set description = 'black'
where c_id = 3;
if (total > 2) then
select o_status,item_total,remarks
from order_status
where item_total = total;
else
select o_status,item_total,remarks,order_no
from order_status
where item_total = total;
update order_status
set remarks = 'set';
end if;
end
I'm expecting the output to get the item_total for each row. If the item_total >2, it will just select, Else, it will update the remarks.. Each c_id have different no. of item_total.
Your code is a bit of a scramble, so I will try to help doing one piece at a time.
Your (1), where c_id > 3, what is the purpose of this, this will give ALL customers with ID greater than 3, but ok.
Formatting your queries for readability is also a good thing to get in the habit of. Also,
USE table aliases, especially when you get into longer table name references, makes it easier
to join/link/get fields/where/group by respectively. Even if one table.
select
c.c_id,
c.lname,
c.address,
c.city,
c.description
from
customer c
where
c.c_id > 3;
Your (2a). Your query does a simple join from the customer table to the order status based on
the customer's ID. This is ok, but it is also returning ALL customers that have an order,
and if someone has multiple orders, it will show that customer for EACH order they have.
Also, try to get in the habit of using JOIN conditions on tables instead of implied joins
within the WHERE clause. It makes it easier when you need to get into left/right join conditions.
select
c.c_id,
c.lname,
c.address,
o.o_status,
o.item_total,
o.remarks,
c.description
from
customer c
JOIN order_status o
ON c.c_id = o.c_id;
Now, if you only cared about a specific customer, add your WHERE clause for said customer ID.
Your (2b) component to update the description to 'black' where c_id =3, that was ok
update customer
set description = 'black'
where c_id = 3;
For #3, the item_total > 2. What is the basis of this item total. You had
declare total int ;
select total = item_total
from order_status;
This should not really do anything as it is going through every order and will just return
1(true) or 0(false) for every record. You have no criteria such as total per customer
or even just the number of orders for a given customer... Such as Person "A" has 5 orders on file,
and Person "B" has 1, Person "C" has 2. You will need to clarify your intent on that.
This poses the follow-up to your if/else based on the item_total > 2. select version(a) or (b)
of yet another query (not going to copy/paste that as no idea your intent)
In your if( total > 2 ) else condition, your update statement has no WHERE clause,
so it will update EVERY record in the order_status table (probably NOT what you intended).
So, with a bit more clarification, maybe some sample data myself and others could help.
Even as a newbie, if there are things that may be confidential otherwise, you can always mask
things by showing only the critical pieces, but not actually show production data, but show
sample data to help us follow your needs... now and in the future.
I have two tables say: user and library. The library contains books sorted in a
certain way; a user can elect to sort his books in a certain way as well. The
structure (and sample data) for the two tables will look thus:
library
bookid position
10 1
12 2
14 3
16 4
user
userid bookid position
12669 12 1
12669 10 2
I want a query to return all the books for user, 12669, sorted by position i.e:
select bookid from user where userid = 12669 group by position
After it has return these sorted books, it should return the other bookids (not present in user) that are in the library. No bookid should be repeated. The result of these scenario will look thus:
12
10
14
16
In other words: All the books in library should be returned by this query but
with the books selected by user sorted according to user.position
I reckon I may need some kind of join statement for this. I tried:
select bookid from user u right join library l on u.bookid = l.bookid where u.userid = 12669 group by u.position
However, I get a syntax error for this. What is the best way to solve this 'problem'? Many thanks.
First of all, your posted query includes in its projection a column name which doesn't belong to either of the tables involved.
Secondly, you are using GROUP BY where sorting is carried out by ORDER BY.
Third point: As #Romil points out, the reference to the USER table in the WHERE clause overrides the outer join, and effectively enforces an inner join. So you need to select from an inline view on the USER table.
Finally, to get the sort order you want, you need to band all the USER.POSITIONs first. This version of your query uses IFNULL to assign an extremely high value to any rows which don't have a joined USER record. So it will sort by all the returned USER.POSITION values then by LIBRARY.POSITION for all the remaining books.
select l.bookid
from ( select * from user
where userid = 12669 ) u
right join library l
on (u.bookid = l.bookid)
order by ifnull(u.position, 999999999) , l.position
NB: if you are indexing the Library of Babel and so might have enough shelves to support more than 999999999 positions just bump up that subsituted value.
"This didn't work for me. It didn't return the bookids in the
arrangement I wanted. "
Frankly I find that surprising. Here is a SqlFiddle which definitely returns your sample data in the order you specify. So I repeat my earlier question: do your posted table descriptions match your actual tables exactly ?
This should do the trick :)
SELECT l.bookid
FROM library l
LEFT OUTER JOIN user u on u.bookid = l.bookid
WHERE u.userid = 12669
ORDER BY isnull(u.position, 99999), l.position
SELECT library.bookid bookid
FROM library
LEFT JOIN (SELECT *
FROM [user]
WHERE [USER].userid = 12669) users
ON [USERs].bookid = library.bookid
ORDER BY [USERs].userid DESC,
[USERs].position,
library.position
Thanks APC, Romil and aF. I finally figured out a solution that didn't use a join.
This worked for me:
(
SELECT bookid
FROM user
WHERE userid = 12669
ORDER BY position
)
UNION (
SELECT bookid
FROM library
)
The first query selects the ordered bookids from user then the union statement selects every other book from library and adds this to the result set. The arrangement of this later group wasn't important.
It's been hours that I've been searching for a solution (books, internet, etc) and can't find anything. Here's my problem:
I got a table of items being tagged by 2 criteria: let's name them crit1 and crit2. For each of the items I can have for crit1 and crit2 the following int example values (criteria1,criteria2): (1,5) (5,2) (4,7) (8,6), etc.
In another table I store users that are subscribed to some of these items filtered by the above criteria. Let's say that user_id 1 is subscribed to the following item type (criteria1, criteria2): (1,5) (2,7). When I make my query to fetch the items that user 1 is subscribed to, I get the items tagged with (1,5) (2,7) but also (1,7) or (2,5). The SQL Select query is making cross-comparisons between each row.
Generally, I would like to know how to make a query that is filtered from more than 1 field in the same row (no cross-row allowed).
I tried to use JOINS to sort the problem but I can't link criteria1 and criteria2 in the same JOIN. I have to use 2 JOINS and that makes them independent (and the cross-comparison between criteria1 and criteria2 will happen).
Use AND operator in ON clause for JOIN:
SELECT i.* FROM subscriptions s
JOIN items i
ON i.crit1 = s.crit1 AND i.crit2 = s.crit2
WHERE s.user_id = 1
without seeing the rest of the structure specifically, I'll give it a shot like...
select
i.itemid,
i.itemDescription,
s.subscriberName
from
items i
join Subscribers s
on i.category1 = s.category1
AND i.category2 = s.category2
If this isn't it, you might need to dump some sample data from each respective table of what you are trying to actually get.
Try this:
multiply crit 1 by 1000 (or some other number-depending or size of numbers) and add to it Crit2
Use that formula in your where or join
I have a database of Facebook Likes from several people. There are duplicate "like_id" fields across many "user_id"s. I want a query that will find the amount of "like_id"s person A has in common with person B.
This query is fantastic for comparing likes when only 2 "user_id"s are in the database, but as soon as I add a 3rd, it messes it up. Basically, I want to see who has the most "likes" in common with with person A.
SELECT *,
COUNT(*)
FROM likes
GROUP BY like_id
HAVING COUNT(*) > 1
Anyone have a query that might work?
This SQL should work. You just need to put in the User A's user_id and it should compare with all other users and show the top matching one. You can change it to show the top 5 or do whatever else you need to do.
Basically what it is doing is that it is doing a self join on the table, but making sure that when it does a join, it is a different user_id but the "like" is the same. Then it does a group by each of the other user_id's and sums the same amount of likes for that user_id.
SELECT all_other_likes.user_id, count(all_other_likes.like_id) AS num_similar_likes
FROM likes original_user_likes
JOIN likes all_other_likes
ON all_other_likes.user_id != original_user_likes.user_id
AND original_user_likes.like_id = all_other_likes.like_id
WHERE original_user_likes = USER_ID_YOU_WANT_TO_COMPARE
GROUP BY all_other_likes.user_id
ORDER BY count(all_other_likes.like_id) DESC
LIMIT 1;
Not sure what database you are using. You might need to do a SELECT TOP 1 if it is MS-SQL, but this is valid PostgreSQL and MySQL syntax.
I think this will do it:
SELECT
likes_a.user_id,
likes_b.user_id
FROM
likes as likes_a JOIN likes as likes_b
ON
likes_a.like_id = likes_b.like_id
WHERE
likes_a.user_id <> likes_b.user_id
And then post-process the results to count up who has the most in common.
I have one sql table that looks like this called "posts":
id | user
--------------------------------
0 | tim
1 | tim
2 | bob
And another called "votes" that stores either upvotes or downvotes on the posts in the "posts" table:
id | postID | type
--------------------------------
0 | 0 | 0
1 | 2 | 1
2 | 0 | 1
3 | 0 | 1
4 | 3 | 0
In this table, the 'type' is either a 0 for downvote or 1 for upvote.
How would I go about ordering posts by "tim" by the number of (upvotes - downvotes) the post has?
SELECT
p.id,
p.user,
SUM(v.type * 2 - 1) AS votecount
FROM posts p
LEFT JOIN votes v ON p.id = v.postID
WHERE p.user = 'tim'
GROUP BY p.id, p.user
ORDER BY votes DESC
UPDATE – p and v explained.
In this query, p and v are aliases of, respectively, posts and votes. An alias is essentially an alternative name and it is defined only within the scope of the statement that declares it (in this case, the SELECT statement). Not only a table can have an alias, but a column too. In this query, votecount is an alias of the column represented by the SUM(v.type * 2 - 1) expression. But presently we are talking only about tables.
Before I go on with explanation about table aliases, I'll briefly explain why you may need to prefix column names with table names, like posts.id as opposed to just id. Basically, when a query references more than one table, like in this case, you may find it quite useful always to prefix column names with the respective table names. That way, when you are revisiting an old script, you can always tell which column belongs to which table without having to look up the structures of the tables referenced. Also it is mandatory to include the table reference when omitting it creates ambiguity as to which table the column belongs to. (In this case, referencing the id column without referencing the posts table does create ambiguous situation, because each table has got their own id.)
Now, a large and complex query may be difficult to read when you write out complete table names before column names. This is where (short) aliases come in handy: they make a query easier to read and understand, although I've already learnt that not all people share that opinion, and so you should judge for yourself: this question contains two versions of the same query, one with long-named table references and the other with short-aliased ones, as well as an opinion (in a comment to one of the answers) why aliases are not suitable.
Anyway, using short table aliases in this particular query may not be as beneficial as in some more complex statements. It's just that I'm used to aliasing tables whenever the query references more than one.
This MySQL documentation article contains the official syntax for aliasing tables in MySQL (which is actually the same as in standard SQL).
Not tested, but should work:
select post.id, sum(if(type = 0, -1, 1)) as score
from posts join votes on post.id = votes.postID
where user = 'tim'
group by post.id
order by score
Do you plan to concur SO? ;-)
Edit: I cut out the subquery since in mysql its unnecessary. The original query was portable, but unnecessary for mysql.
select
p.id, SUM(case
when v.type = 0 then -1
when v.type = 1 then 1
else 0 end) as VoteCount
from
posts p
left join votes v
on p.id = v.postid
where
p.[user] = 'tim'
group by
p.id
order by
VoteCount desc