Stored procedure counting trouble - mysql

I have a table [users] that I wish to count the number of each occurrence of Movie_ID and update the record in a different table called [total]. So for Movie_ID=81212 it would send the value 2 to my [total] table.
like below:
------------------------------------
| [users] | [total]
+---------+---------+ +---------+-------------+
|Movie_ID |Player_ID| |Movie_ID | Player_Count|
+---------+---------+ +---------+-------------+
|81212 |P3912 | | 81212 | 2 |
+---------+---------+ +---------+-------------+
|12821 |P4851 | | 12821 | 1 |
+---------+---------+ +---------+-------------+
|81212 |P5121 |
+---------+---------+
(movie_ID + player_ID form composite key
so Movie_ID does not need to be unique)
So i'm trying to accomplish this with a stored procedure, this is what I have so far: I'm not sure how to code the part where it loops through every entry in the [users] table in order to find each occurrence of movie_id and sums it up.
DELIMITER //
CREATE PROCEDURE `movie_total` (OUT movie_count int(5))
LANGUAGE SQL
MODIFIES SQL DATA
BEGIN
DECLARE movie_count int(5);
SELECT count(movie_id) AS movie_count FROM users
foreach unique row in Users ;
IF (SELECT COUNT(*) FROM users WHERE movie_id) > 0
THEN
INSERT INTO total (:movie_id, :Player_Count) VALUES (movie_id, movie_count);
END //

To update this field you can use a query like this -
UPDATE
total t
JOIN (SELECT Movie_ID, COUNT(*) cnt FROM users GROUP BY Movie_ID) m
ON t.Movie_ID = m.Movie_ID
SET
t.Player_Count = cnt
BUT: Do you really need a total table? You always can get this information using SELECT query; and the information in the total table may be out of date.

I think you can do this without a loop:
update total set total.Player_Count = (select COUNT(Movie_ID) from users where total.Movie_ID=users.Movie_ID group by (Movie_ID));

Related

getting data from multiple tables and applying arithmatic operation on the result

I want to fetch data from two table and apply arithmetic operation on the column.
This is wha I tried :
String sql = "SELECT SUM(S.san_recover-C.amount) as total
FROM sanction S
LEFT JOIN collection C ON S.client_id = C.client_id
WHERE S.client_id=?";
This code is working only when there is value in both tables, but if there is no value in one of two tables there is no result.
SELECT SUM(S.san_recover - C.amount) as total
FROM sanction S
LEFT JOIN collection C ON S.client_id = C.client_id
WHERE S.client_id = ?
The problem with your query lies in the SUM() function. When the left join does not bring back records, then c.amount is NULL. When substracting NULL from something, you get a NULL result, which then propagates across the computation, and you end up with a NULL result for the SUM().
You probably want COALESCE(), like so:
SELECT SUM(S.san_recover - COALESCE(C.amount, 0)) as total
FROM sanction S
LEFT JOIN collection C ON S.client_id = C.client_id
WHERE S.client_id = ?
Where there is a possibility that a client may exist in one table but no another a full join would be appropriate but since mysql does not have such a thing then a union in a sub query will do
drop table if exists sanctions,collections;
create table sanctions(client_id int, amount int);
create table collections(client_id int, amount int);
insert into sanctions values
(1,10),(1,10),(2,10);
insert into collections values
(1,5),(3,10);
Select sum(Samount - camount)
From
(Select sum(amount) Samount, 0 as camount from sanctions where client_id =3
Union all
Select 0,sum(amount) as camount from collections where client_id =3
) s
;
+------------------------+
| sum(Samount - camount) |
+------------------------+
| -10 |
+------------------------+
1 row in set (0.00 sec)
If you want to do this for all clients
Select client_id,sum(Samount - camount) net
From
(Select client_id,sum(amount) Samount, 0 as camount from sanctions group by client_id
Union all
Select client_id,0,sum(amount) as camount from collections group by client_id
) s
group by client_id
;
+-----------+------+
| client_id | net |
+-----------+------+
| 1 | 15 |
| 2 | 10 |
| 3 | -10 |
+-----------+------+
3 rows in set (0.00 sec)

MySQL JOIN two tables by one value and take other results (without that value too)

I have some problem with a mySQL query.
The table A is this:
A.id
A.value1
A.user
Table B is:
B.id
B.user
I need to find value_that_i_need from query, by searching for B.user.
But I don't need only values with A.user, i need all values from Table A with the same A.id (inside Table A) that matches B.user.
So I need all distinct id (where there is B.user=A.user) and search for them inside table A by A.id.
I want to avoid 2 different queries! Already tried differents JOIN, nothing works for me.
EDIT
Ok, i will ty to explain the problem in a easiest way.
I have this table:
+---------+------------+
| id_user | another_id |
+---------+------------+
id_user -> unique id for each user
another_id -> an id related to something like a group
another_id can be the same to more users, but i need to take only users who are inside my same groups.
So i will have to check my groups (by searching my id_user) and then i have to see all users with my same another_id.
Problem is that if i query something like this:
SELECT * FROM table0 AS t0, something_like_groups AS slg
JOIN user_inside_group as uig ON slg.id_group=uig.group_id AND slg.id_user='my_user_id'
WHERE slg.id='id_group' AND t0.user_id=uig.user_id
Actually i have to join 3 tables, but the problem is that i need to find the "group" inside i am and get ALL informations about all users inside my same group. (without an additional query)
Perhaps you just want to find the min id based on b user and then get all the rows from a which match. for example
drop table if exists t,t1;
create table t( id int,user varchar(10));
create table t1( id int,user varchar(10));
insert into t values
(1,'aaa'),(1,'bbb'),(2,'ccc');
insert into t1 values
(1,'bbb'),(2,'ccc')
;
select t.id,t.user
from t
join
(
select t1.user,min(t.id) minid
from t1
join t on t.user = t1.user
group by t1.user
) s
on t.id = s.minid;
+------+------+
| id | user |
+------+------+
| 1 | aaa |
| 1 | bbb |
| 2 | ccc |
+------+------+
3 rows in set (0.00 sec)

SQL query relating how many of each collection set a given consumer has

Say I have customers who can be awarded certain prizes:
SELECT gs.claimed_by AS consumer_id, p.prize_id AS prize_id FROM
awarded_prizes
And right now, customer 1 has three prizes and customer 2 has a single prize
+-------------+----------+
| consumer_id | prize_id |
+-------------+----------+
| 1 | 45 |
| 1 | 46 |
| 1 | 47 |
| 2 | 66 |
+-------------+----------+
Say we also have collections, and if you collect all the members to that collectible, you now have a collectable set:
SELECT set_id, member_prize_id AS prize_id FROM collectable_set_members;
+--------+----------+
| set_id | prize_id |
+--------+----------+
| 1 | 45 |
| 1 | 46 |
| 1 | 47 |
| 2 | 65 |
| 2 | 66 |
+--------+----------+
With the above table and the previous query, we can see customer 1 has completed set 1 once (they have 45, 46, 47) and customer 2 has completed nothing.
There are cases where a customer can complete a set multiple times (customer could have 45, 46, 47, 45, 46, 47 in the awarded_prize table.
I've been looking at the pantry problem and its variations (like the bartender problem), have been playing with cross joins and groupings and can't seem to find what I want.
I'm trying to get a result for a given customer, showing all the set_ids they own and the number of sets they've completed:
+-------------+---------------+--------+
| consumer_id | completed_set | count |
+-------------+---------------+--------+
| 1 | 1 | 1 |
+-------------+---------------+--------+
I'm on mariadb:5.5
See here SqlFiddle
My tables have different names than yours, but it proves the point:
select sets_x_consumers.consumer_id, sets_x_consumers.set_id,
set_summary.items_in_set = consumer_summary.items_per_set_per_consumer as set_is_complete
from (
-- build a cross-product of sets and consumers
select distinct set_id, consumer_id
from sets join consumers -- no join condition -> cross product
) sets_x_consumers
inner join
( -- the total number of items in each set per set_id
select set_id, count(*) items_in_set
from sets
group by set_id
) set_summary on sets_x_consumers.set_id = set_summary.set_id
inner join
( -- the total number of items per set and customer
select set_id, consumer_id, count(*) items_per_set_per_consumer
from sets
inner join consumers on sets.prize_id = consumers.prize_id
group by consumer_id, set_id
) consumer_summary on sets_x_consumers.set_id = consumer_summary.set_id and sets_x_consumers.consumer_id = consumer_summary.set_id
My basic idea is to sum up the number of items in each set and the number of items per set each consumer has claimed. As long as there are no duplicate entries for the pair of consumer and prize, this should work (if duplicates were allowed, I would use count distinct(prize_id) for the consumer_summary).
The output of the query above is:
| consumer_id | set_id | set_is_complete |
|-------------|--------|-----------------|
| 1 | 1 | 1 |
| 2 | 2 | 0 |
This lists each pair of consumers and set for which a consumer has at least one prize. (to change this to list every consumer-set combination, use outer join)
Listing only complete sets or summarizing the number of complete sets should be easy on this basis ;-)
Can't really figure out what your last column 'count' is supposed to mean,
but here is a solution that lists users and their sets completed.
demo Link
The whole idea is to count the number of prizes required for each set, and count the collected prizes per customer per set, and thus you can join the two.
I know it's mssql, but I did not manage to make mysql ctes work in sqfiddle.
CTE-s is nothing more than a subquery basically. If your server does not support CTE-s you could use normal subqueries or temp tables instead.
For what it's worth I came up with a nice routine for this in Sql Server. This works even if there are overlapping prize_id values in each set (will default to higher setid if ambiguous). Assume all temp tables are original data:
declare #awarded_prize table (rowid int identity, consumer_id int, prize_id int )
insert #awarded_prize
select * from #awarded_prizes
declare #collections table ( set_id int, prize_id int, rownumber int , filled int)
insert #collections
select *, row_number() over(partition by set_id order by set_id, prize_id) , null
from #collections
declare #todelete table (rowid int)
declare #scorecard table (consumer_id int, set_id int)
declare #iterator int=1
declare #prize_id int
declare #set_id int = (Select min(set_id) from #collections)
declare #consumer_id int = (Select min(consumer_id) from #awarded_prize)
while #consumer_id<=(select max(consumer_id) from #awarded_prize)
begin
while #set_id<=(select max(set_id) from #collections)
begin
while 1=1
begin
select #prize_id=prize_id
from #collections
where set_id=#set_id and rownumber=#iterator
if (select max(rowid) from #awarded_prize where prize_id=#prize_id and consumer_id=#consumer_id and rowid not in (select rowid from #todelete)) is null break
insert #todelete
select max(rowid) from #awarded_prize where prize_id=#prize_id and consumer_id=#consumer_id and rowid not in (select rowid from #todelete)
update #collections set filled=1
where rownumber=#iterator and set_id=#set_id
if not exists(select 1 from #collections where set_id=#set_id and filled is null)
begin
insert #scorecard
select #consumer_id, #set_id
delete #awarded_prize where rowid in (Select rowid from #todelete)
delete #todelete
update #collections set filled=null where filled=1
end
set #iterator=case when #iterator=(Select max(rownumber) from #collections where set_id=#set_id) then
(select min(rownumber) from #collections where set_id=#set_id) else #iterator+1 end
end
delete #todelete
set #iterator=1
set #set_id=#set_id+1
end
set #iterator=1
select #set_id=min(set_id) from #collections
select #consumer_id=min(consumer_id) from #awarded_prize where consumer_id>#consumer_id
end
select consumer_id, set_id, count(*) complete_sets
from #scorecard
group by consumer_id, set_id
order by consumer_id, set_id

How to write select query along with IF condition in place of column name in mysql query

SELECT DISTINCT o.receipt,
if(SELECT status FROM list WHERE receipt = o.receipt GROUP BY receipt) as status
FROM orderlist o
What is the correct way to write the above query If condition and get result like below example.
Same receipt(orderId) has more than one tuple(row) and all this tuple might have different status value.And I want to set value as per receipt(orderId) first tuple status using IF statement.
IF(status = 'shipped', "Yes", "NO");
If you data model looks like this (ie you have a way of distinguishing the order of events) then a limit in your subquery might do.
drop table if exists t,t1;
create table t(id int);
create table t1(id int, tid int, status varchar(10));
insert into t values (1),(2);
insert into t1 values (1,1,'a'),(2,1,'Shipped'),(3,1,'Returned'), (4,2,'shipped');
select t.id,
if(
(select status from t1 where t1.tid = t.id order by id limit 1)
= 'Shipped','yes','no') shipped
from t;
Result
+------+---------+
| id | shipped |
+------+---------+
| 1 | no |
| 2 | yes |
+------+---------+
2 rows in set (0.00 sec)
But isn't shipment usually the last status?
Try this.
SELECT DISTINCT o.receipt, CASE status when 'Shipped' then 'Yes' ELSE 'No' END as status
FROM orderlist o join receipt r on o.receipt = r.receipt

Deleting database existing record while asigning values from one row to other with unique values

EDIT2: Solved Thanks all for fast reply, appreciate ur help. Specially to Mr Jeremy Smyth for the working solution.
I'm fairly new to sql and cant find a solution to make an update query. I have the following table
Table: order
id | cid | pid
1 | 1 | a1
2 | 1 | a2
3 | 2 | a2
4 | 2 | a3
5 | 2 | a4
I want the cid of 2 to become 1, BUT not updating rows which have same pid i.e(id.2 & id.3).
The result i want is:
id | cid | pid
1 | 1 | a1
2 | 1 | a2
3 | 2 | a2
4 | '1' | a3
5 | '1' | a4
pseudo query example: UPDATE order SET cid=1 WHERE cid=2 AND 1.pid <> 2.pid;
EDIT1: not to confuse pid values with cid and id i changed them with 'a' in start. as suggested i'll not use order as table name.
On update I simply dont want duplicate pid for cid
Sorry for bad English.
I hope I understood you right:
UPDATE `order`
SET cid = 1
WHERE cid = 2
AND cid <> pid
What do you think?
Please notice: ORDER is a reserved word, read more.
I think you need something like this.
UPDATE order SET cid=1 WHERE cid=2 AND cid <> pid;
This can only be done in multiple steps (i.e. not a single UPDATE statement) in MySQL, because of the following points
Point 1: To get a list of rows that do not have the same pid as other rows, you would need to do a query before your update. For example:
SELECT id FROM `order`
WHERE pid NOT IN (
SELECT pid FROM `order`
GROUP BY pid
HAVING COUNT(*) > 1
)
That'll give you the list of IDs that don't share a pid with other rows. However we have to deal with Point 2, from http://dev.mysql.com/doc/refman/5.6/en/subquery-restrictions.html:
In general, you cannot modify a table and select from the same table in a subquery.
That means you can't use such a subquery in your UPDATE statement. You're going to have to use a staging table to store the pids and UPDATE based on that set.
For example, the following code creates a temporary table called badpids that contains all pids that appear multiple times in the orders table. Then, we execute the UPDATE, but only for rows that don't have a pid in the list of badpids:
CREATE TEMPORARY TABLE badpids (pid int);
INSERT INTO badpids
SELECT pid FROM `order`
GROUP BY pid
HAVING COUNT(*) > 1;
UPDATE `order` SET cid = 1
WHERE cid= 2
AND pid NOT IN (SELECT pid FROM badpids);