Stored Procedure combining resultsets - mysql

When I run below mysql stored procedure, I get three different tables as output. Is there any way, I can combine these three tables and display output as in a single table with different columns and single row ?
CREATE DEFINER=`root`#`localhost` PROCEDURE `retrieveApplicantStatus`(IN in_userId INT)
BEGIN
SELECT applicants_id, approval FROM applicants WHERE users_id = in_userId;
SELECT pass_fail.result AS test_result, pass_fail.license_approval FROM pass_fail WHERE user_id = in_userId;
SELECT result AS trial_result FROM trial_result WHERE user_id = in_userId;
END
Required Output :
--------------------------------------------------------------------------
applicants_id | approval | test_result | trial_result | license_approval |
--------------------------------------------------------------------------
| | | | |
--------------------------------------------------------------------------

If user_id is constrainted to be unique in each table then you can use:
CREATE DEFINER=`root`#`localhost` PROCEDURE `retrieveApplicantStatus`(IN in_userId INT)
BEGIN
SELECT u.in_userId,
a.applicants_id,
a.approval,
pf.result AS test_result,
pf.license_approval,
tr.result AS trial_result
FROM (SELECT in_userId) AS u
LEFT JOIN applicants AS a
ON a.users_id = u.in_userId
LEFT JOIN pass_fail AS pf
ON pf.user_id = u.in_userId
LEFT JOIN trial_result AS tr
ON tr.user_id = u.in_userId;
END
HOWEVER If it is not constrained to be unique this will give you a cartesian product, i.e. 2 rows in each table will give you 8 rows in total, 3 rows in each table will give you 27 results.

Try to use following code:-
BEGIN
SELECT applicants_id, approval
FROM applicants WHERE users_id = in_userId;
UNIION ALL
SELECT pass_fail.result AS test_result, pass_fail.license_approval
FROM pass_fail
WHERE user_id = in_userId;
UNION ALL
SELECT result AS trial_result
FROM trial_result
WHERE user_id = in_userId;
END

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)

Update MySQL Table from Subquery/Joined Same Table

I've seen many questions along this issue, but can't get this to work.
I want to UPDATE multiple columns in a table (but will start with one) based upon a calculated value from the same table.
It is a list of transactions per customer, per month.
TransID | Cust | Month | Value | PastValue | FutureValue
1 | 1 | 2018-01-01 | 45 |
2 | 1 | 2018-02-01 | 0 |
3 | 1 | 2018-03-01 | 35 |
4 | 1 | 2018-04-01 | 80 |
.
UPDATE tbl_transaction a
SET PrevMnthValue =
(SELECT COUNT(TransactionID) FROM tbl_transaction b WHERE b.Cust=a.Cust AND b.Month<a.Month)
But we get the dreaded 'Can't update a table using a where with a subquery of the same table).
I've tried to nest the subquery as this has been touted as a workaround:
UPDATE tbl_transactions a
SET
PastValue =
(
SELECT CNT FROM
(
SELECT
COUNT(TransactionID) AS CNT
FROM tbl_transactions b
WHERE
b.CustomerRef=a.CustomerRef AND b.Month<a.Month
) x
),
FutureValue =
(
SELECT CNT FROM
(
SELECT
COUNT(TransactionID) AS CNT
FROM tbl_transactions b
WHERE
b.CustomerRef=a.CustomerRef AND b.Month>a.Month
) x
)
But I get an UNKNOWN a.CustomerRef in WHERE clause. Where am I going wrong?
You can't update and read from one table at the same time.
MySQL documentation tell about it
You cannot update a table and select from the same table in a subquery.
At first you must select necessary data and save them to somewhere, for example to temporary table
CREATE TEMPORARY TABLE IF NOT EXISTS `temp` AS (
SELECT
COUNT(`TransactionID`) AS CNT,
`CustomerRef`,
`Month`
FROM `tbl_transactions`
GROUP BY `Custom,erRef`, `Month`
);
After it, you can use JOIN statement for update table
UPDATE `tbl_transactions` RIGTH
JOIN `temp` ON `temp`.`CustomerRef` = `tbl_transactions`.`CustomerRef`
AND `temp`.`Month` < `tbl_transactions`.`Month`
SET `tbl_transactions`.`PastValue` = `temp`.`cnt`
UPDATED: if you want to update several columns by different condition you can combine temporary table, UPDATE + RIGHT JOIN and CASE statement. For example:
UPDATE `tbl_transactions`
RIGTH JOIN `temp` ON `temp`.`CustomerRef` = `tbl_transactions`.`CustomerRef`
SET `tbl_transactions`.`PastValue` = CASE
WHEN `temp`.`Month` < `tbl_transactions`.`Month` THEN `temp`.`cnt`
ELSE `tbl_transactions`.`PastValue`
END,
`tbl_transactions`.`FutureValue` = CASE
WHEN `temp`.`Month` > `tbl_transactions`.`Month` THEN `temp`.`cnt`
ELSE `tbl_transactions`.`FutureValue`
END
You can try below
UPDATE tbl_transactions a
Join
( SELECT CustomerRef,COUNT(TransactionID) AS CNT FROM tbl_transactions b
group by CustomerRef)x
SET PastValue = CNT
WHERE x.CustomerRef=a.CustomerRef AND x.Month<a.Month

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

Stored procedure counting trouble

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));

sql matching users that have activities in common

so, i have a table with users and activities
users
id | activities
1 | "-2-3-4-"
2 | "-3-4-"
3 | "-1-2-3-4-"
activities
id | title
1 | running
2 | walking
3 | climbing
4 | singing
and I am trying for a user with id 3 to find users that have at least two same activities
what I tried to do is this
SELECT u.id FROM users u
WHERE ( SELECT COUNT(a.id) FROM activities a
WHERE a.id IN(TRIM( ',' FROM REPLACE( u.activities, '-', ',' ) ))
AND a.id IN(1,2,3) ) >= 2
any ideas?
For the love of god, make a 3rd table that contains user_id and activity_id.
This isn't a suitable solution in any way.
You should have a table which makes the connection between users and activities, not store all activities in a row in your users table.
You can first create a function that takes the user.activities string and splits the string in activities int ids like this:
create FUNCTION dbo.SplitStringToIds (#acts nvarchar(MAX))
RETURNS #acivityids TABLE (Id int)
AS
BEGIN
DECLARE #stringToInsert nvarchar (max)
set #stringToInsert=''
DECLARE #intToInsert int
set #intToInsert=0
DECLARE #stidx int
set #stidx=0
DECLARE #endidx int
set #endidx=0
WHILE LEN(#acts) > 3
BEGIN
set #stidx=CHARINDEX('-', #acts, 1)
set #acts=substring(#acts,#stidx+1,len(#acts))
set #endidx=CHARINDEX('-', #acts, 1)-1
set #stringToInsert=substring(#acts,1,#endidx)
set #intToInsert=cast(#stringToInsert as int)
INSERT INTO #acivityids
VALUES
(
#intToInsert
)
END
-- Return the result of the function
RETURN
END
GO
and then you can try something like this to get the users that have 2 and more same activities with user with id=3
select u.id,count(u.id) as ActivitiesCounter from users as u
cross apply SplitStringToIds(u.activities) as v
where v.id in (select v.id from users as u
cross apply SplitStringToIds(u.activities) as v
where u.id=3)
group by u.id having count(u.id)>=2
But i think that this way of storing relationships between tables is going to give you only troubles and its better to add a relationship table if you can.