Stored Procedure in data mining prediction - mysql

I am trying to write a stored procedure to display (fid, name,tel,income,balance,loan_amount, child_count) from two tables
Family(fid,name,tel,income,good_credit,balance)
Children(cid,name,age,fid). The function should be able to predict who can be a potential customer based on the following criteria:
Good credit, balance>=3000, child_count:total number of children between age 18 and 22, income:40000 and loan_amount = income * 0.3 * child_count
Family table
fid name tel income good_credit balance
1 aaa1 111 15000 1 1000
2 aaa2 211 25000 0 10000
3 aaa3 311 80000 0 1000
4 aaa4 411 40000 1 7000
Children table
cid name age fid
1 bbb1 14 3
2 bbb2 14 15
3 bbb3 19 16
4 bbb4 18 9
My code looks like this, but i got an error "subquery returns more than one row"
delimiter $$
CREATE PROCEDURE procc()
BEGIN
declare loan_amount int (200);
declare child_count int (200);
set child_count = (select count(age) from Children,Family where age between 18 and 22 );
set loan_amount = (select income from Children CV,Family DV where DV.FID = CV.FID);
if (child_count is not null) then select cf.FID as FID, cf.Name as Name, cf.TEL as TEL, cf.income as income, cf.balance as balance, child_count, loan_amount * 0.3 * child_count from Family cf,Children cc
where cf.FID = cc.FID and good_credit = 1 and income >= 40000 and balance = 3000;
end if;
end$$
delimiter ;

Related

Return values for a time series with last known value for each day in MySQL

In MySQL 5.7 I am trying to retrieve price data from a table for each day. The table contains only price changes.
DATE | SKU | PRICE
2020-01-15 123 4.99
2020-01-10 123 3.99
If no change has occured on a particular day, we can assume the price has stayed the same. My objective is to create a graf in grafana displaying the price over time.
The desired output is therefore:
DATE | SKU | PRICE
2020-01-15 123 4.99
2020-01-14 123 3.99
2020-01-13 123 3.99
2020-01-12 123 3.99
2020-01-11 123 3.99
2020-01-10 123 3.99
After some investigation I create a stored procedure that joins against a tmp table with dates:
delimiter //
create
definer = root#localhost procedure sp1(IN d1 date, IN d2 date)
BEGIN
declare d datetime;
create temporary table if not exists foo (d date not null);
set d = d1;
while d <= d2 do
insert into foo (d) values (d);
set d = date_add(d, interval 1 day);
end while;
select
foo.d,
p.*
from foo
left join prices_test2 p on foo.d = p.date
order by
foo.d asc;
drop temporary table foo;
END//
Then simply call it like this:
call sp1(210110, 210122)
Now the issue is, that this procedure returns NULL for any date without price but it should return the last known price for the SKU.
How can this be achieved?

MYSQL query, loop all rows, based on specific field value run loop with another query

I have 2 tables as the follows:
Purchases:
Purchase_id
Stations
1
4
2
6
And another table:
allItems:
Item_id
Purchase_id
Product_id
Im doing a query to get all purchases with no NULL value under Stations:
SELECT * FROM Purchases WHERE Stations IS NOT NULL
Let's say I got the Purchases table above and I want to start go through it and insert into the allItems table a row for each station.
For example the first row I got has a Stations value of 4, I want to insert into the aallItems table the following row 4 times, the second row where Stations value is 6, i'd like to insert the following 6 times.
INSERT INTO allItems(Purchase_id, Products_id) VALUES (??Not sure what to insert here i need the Purchase_id from the row??, 17)
expected outcome should be the added last 4 rows:
allItems:
Item_id
Purchase_id
Product_id
1
1
17
2
1
17
3
1
17
4
1
17
5
2
17
6
2
17
7
2
17
8
2
17
9
2
17
10
2
17
Any help will be appreciated
I think you should write a stored procedure for that like this:
CREATE PROCEDURE FILLSP()
BEGIN
DECLARE counter INT DEFAULT 0;
DECLARE count INT;
DECLARE repeat INT;
DECLARE pid INT;
DECLARE i INT;
SELECT COUNT(*) FROM Purchases INTO count;
WHILE counter < count DO
SELECT Stations FROM Purchases LIMIT counter,1 INTO repeat;
SELECT Purchase_id FROM Purchases LIMIT counter,1 INTO pid;
SET i = 0;
WHILE i < repeat DO
INSERT INTO allItems VALUES (pid, 17);
SET i = i + 1;
END WHILE;
SET counter = counter + 1;
END WHILE;
END

select query for billing statement

I have to design an optimized MySQL query for generating a report based on 2 tables.
I have 2 tables one for services and another for payments. I accept user specific criteria for services and based on that I have to report services and corresponding payments. These transactions from 2 different tables will be in order of service dates and corresponding payments in order of payment dates. Along with that, I also have to report any advance payments paid on account (in database terms payments not linked to any particular service)
Currently I run one query for selecting services and unlinked payments using UNION of 2 tables based on given criteria. Then I run separate query for each service related payment through a loop.
Is there any way I can get all these transactions via a single query and that too in desired order.
Here are the relevant columns of 2 tables.
service table
id (PK)
account_no
date
service_amount
tran_type
payment table
id
account_no
date
pmt_amount
service_id (FK to service table nulls acceptable)
tran_type
Here are the queries I am trying
Query 1
select account_no, id, date, service_amount, tran_type
from service where <user specified criteria like date range>
UNION
select account_no, id, date, pmt_amount, tran_type
from payment where service_id is null and
<user specified criteria like date range>
order by date
Query2
This query is run on individual services on result of above query ( tran_type is service)
select account_no, id, date, pmt_amount, tran_type
from payment where service_id= <specific id>
order by date
Service table Data
ID Item_Typ Date Amt Acct#
1 SVC 11/12/2015 10 1
2 SVC 11/20/2015 20 1
3 SVC 12/13/2015 40 1
4 SVC 4/1/2016 30 1
Payment table Data
ID Svc_ID Item_Typ Date Amt Acct#
1 1 PMT 11/15/2015 5 1
2 1 PMT 11/15/2015 5 1
3 2 PMT 11/25/2015 40 1
4 3 PMT 12/28/2015 35 1
5 2 PMT 12/30/2015 -15 1
7 NULL PMT 1/1/2016 12 2
8 NULL PMT 3/1/2016 35 3
Query 1 Result
ID Item_Typ Date Amt Acct#
1 SVC 11/12/2015 10 1
2 SVC 11/20/2015 20 1
3 SVC 12/13/2015 40 1
4 SVC 4/1/2016 30 1
7 PMT 1/1/2016 12 2
8 PMT 3/1/2016 35 3
Final result after fetching payments for all query result related services
tranTyp Date Amt Acct#
SVC 11/12/2015 10 1
PMT 11/15/2015 5 1
PMT 11/15/2015 5 1
SVC 11/20/2015 20 1
PMT 11/25/2015 40 1
PMT 12/30/2015 -15 1
SVC 12/13/2015 40 1
PMT 12/28/2015 35 1
drop table if exists service;
create table service (ID int, Item_Typ varchar(3), `Date` date, Amt int, Acct int);
insert into service values
(1, 'SVC', '2015-11-12', 10 , 1),
(2, 'SVC', '2015-11-20', 20 , 1),
(3, 'SVC', '2015-12-13', 40 , 1),
(4, 'SVC', '2016-01-04', 30 , 1),
(5, 'SVC', '2015-10-04', 50 , 1)
drop table if exists payment;
create table payment(ID INT, Svc_ID INT, Item_Typ VARCHAR(3), `Date` DATE, Amt INT, Acct INT);
INSERT INTO payment values
(1, 1 , 'PMT', '2015-11-15', 5 , 1),
(2, 1 , 'PMT', '2015-11-15', 5 , 1),
(3, 2 , 'PMT', '2015-11-25', 40 , 1),
(4, 3 , 'PMT', '2015-12-28', 35 , 1),
(5, 2 , 'PMT', '2015-12-30', -15 ,1),
(7, NULL , 'PMT', '2016-01-01', 12 , 2),
(8, NULL , 'PMT', '2016-03-01', 35 , 3);
MariaDB [sandbox]> select * from
-> (
-> select 1 as typ,id,Item_typ,`date`, `date` as svc_date,amt,acct from service
-> union all
-> select 2,p.svc_id,p.Item_typ,p.`date`,
-> case when s.id is null then now()
-> else s.`date`
-> end as svc_date,
-> p.amt, p.acct from payment p
-> left join service s on p.svc_id = s.id
-> ) s
->
-> order by s.svc_date,s.acct,s.typ,s.id
-> ;

Mysql best students in every class in a school

In MySql I need to select top student in every class in a school in termid=10 to get discount for next term enrollment .
Please notice that total is not in table(I put in below for clearing problem)
I have this workbook table for all students workbook:
id studentid classid exam1 exam2 total termid
1 2 11 20 40 60 10
2 1 22 40 20 60 10
3 4 11 40 20 60 10
4 5 33 10 60 70 10
5 7 22 10 40 50 10
6 8 11 10 30 40 10
7 9 33 20 45 65 10
8 11 11 null null null 10
9 12 54 null null null 02
10 13 58 null null null 02
1st challenge is : exam1 and exam2 are VARCHAR and total is not in table (as i explained).
2nd challenge is : as you can see in id=8 std #11 has not numbers
3rd challenge is : may be two students have top level so they must be in result.
I need result as :
id studentid classid exam1 exam2 total termid
1 2 11 20 40 60 10
3 4 11 40 20 60 10
4 5 33 10 60 70 10
2 1 22 40 20 60 10
i have this query but not work good as i mention.
SELECT DISTINCT id,studentid,classid,exam1,exam2,total,termid ,(CAST(exam1 AS DECIMAL(9,2))+CAST(exam2 AS DECIMAL(9,2))) FROM workbook WHERE ClassId = '10';
You can get the total for the students by just adding the values (MySQL will convert the values to numbers). The following gets the max total for each class:
select w.classid, max(coalesce(w.exam1, 0) + coalesce(w.exam2, 0)) as maxtotal
from workbook w
group by w.classid;
You can then join this back to the original data to get information about the best students:
select w.*, coalesce(w.exam1, 0) + coalesce(w.exam2, 0) as total
from workbook w join
(select w.classid, max(coalesce(w.exam1, 0) + coalesce(w.exam2, 0)) as maxtotal
from workbook w
group by w.classid
) ww
on w.classid = ww.classid and (coalesce(w.exam1, 0) + coalesce(w.exam2, 0)) = ww.maxtotal;
Another approach is to join the table with itself. You find out the max for each class and then join all students of this class which match the class max:
max for each class (included in the final statement already):
SELECT classid, MAX(CAST(exam1 AS UNSIGNED) + CAST(exam2 AS UNSIGNED)) as 'maxtotal'
FROM students
WHERE NOT ISNULL(exam1)
AND NOT ISNULL(exam2)
GROUP BY classid
The complete statement:
SELECT s2.*, s1.maxtotal
FROM (SELECT classid, MAX(CAST(exam1 AS UNSIGNED) + CAST(exam2 AS UNSIGNED)) as 'maxtotal'
FROM students
WHERE NOT ISNULL(exam1)
AND NOT ISNULL(exam2)
GROUP BY classid) s1
JOIN students s2 ON s1.classid = s2.classid
WHERE s1.maxtotal = (CAST(s2.exam1 AS UNSIGNED) + CAST(s2.exam2 AS UNSIGNED));
SQL Fiddle: http://sqlfiddle.com/#!2/9f117/1
Use a simple Group by Statement:
SELECT
studentid,
classid,
max(coalesce(exam1,0)) as max_exam_1,
max(coalesce(exam2,0)) as max_exam_2,
sum(coalesce(exam1,0) + coalesce(exam2,0)) as sum_exam_total,
termid
FROM
workbook
WHERE
termid=10
GROUP BY
1,2
ORDER BY
5
Try something like this:
SELECT id,studentid,classid,exam1,exam2,(CAST(exam1 AS DECIMAL(9,2))+CAST(exam2 AS DECIMAL(9,2))) AS total,termid FROM `workbook` WHERE ((CAST(exam1 AS DECIMAL(9,2))+CAST(exam2 AS DECIMAL(9,2)))) > 50
Thanks all my friends
I think combine between 2 answer in above is best :
SELECT s2.*, s1.maxtotal
FROM (SELECT ClassId, MAX(
coalesce(exam1,0)+
coalesce(exam2,0)
) as 'maxtotal'
FROM workbook
WHERE
(
termid = '11'
)
GROUP BY ClassId) s1
JOIN workbook s2 ON s1.ClassId = s2.ClassId
WHERE s1.maxtotal = (
coalesce(exam1,0)+
coalesce(exam2,0)
) AND (s1.maxtotal >'75');
last line is good for s1.maxtotal=0 (some times student scores have not be entered and all equals 0 so all will shown as best students) or some times we need minimum score (to enroll in next term).
So thanks all

Getting mySQL stored procedure results in another stored procedure

I need an example of a stored procedure that uses the results of two another stored procedures (or even just one) where the results are used as tables in the combining stored procedure. I could likely take it from there.
examples on web (multiple sites) and details at php.net are too simplistic.
The basic issue can be seen here:
SELECT ttd.person_id, weight, min(test_date)
FROM (
CALL get_team_member_body_stats(1) AS tbs,
CALL get_team_member_first_last_test_date(1) AS ttd
WHERE tbs.person_id = ttd.person_id
AND (tbs.test_date = ttd.first_test OR tbs.test_date = ttd.last_test)
GROUP BY ttd.person_id;
END
Thanks for any help. I've beat my head against this for several hours yesterday and today in online searching and experimenting.
Details:
Table 1
stored procedure get_team_member_body_stats(IN team_id INT) works over 5 tables (person, team, person_team, body_stats, person_body_stats) and produces:
person_id | body_stats_id | weight | test_date |
2 2 200 2011-01-01
4 3 250 2011-01-01
1 5 145 2011-03-01
2 6 210.4 2011-03-01
5 7 290 2011-03-01
1 8 140 2011-04-01
1 9 135 2011-05-01
4 11 245 2011-05-01
Table 2
stored procedure get_team_member_first_last_test_date(IN team_id INT) works over the same tables and produces:
person_id | first_test | last_test
1 2011-03-01 2011-05-01
2 2011-01-01 2011-03-01
4 2011-01-01 2011-05-01
5 2011-03-01 2011-03-01
Goal is to join these and produce:
Table 3
person_id | first_weight | last_weight
1 145 135
2 200 210.4
4 250 245
5 290 290
Thanks
It appears (per this community and because I couldn't find any examples of stored procedures using result sets from other stored procedures as tables in new select tables) that mysql either doesn't support this or that it is ridiculously hard to do what I hoped to do.
So rather than calling stored procedures I copied the queries into this final procedure.
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `get_team_stats_change`(
IN team_id INT
)
BEGIN
SELECT SUM(start_weight) AS start_weight,
SUM(start_body_fat) AS start_body_fat,
SUM(current_weight) AS current_weight,
SUM(current_body_fat) AS current_body_fat,
SUM(weight_change) AS weight_change,
SUM(body_fat_change) AS body_fat_change FROM (
SELECT ls.person_id,
fs.weight AS start_weight, fs.body_fat AS start_body_fat,
ls.weight AS current_weight, ls.body_fat AS current_body_fat,
fs.weight - ls.weight AS weight_change, fs.body_fat - ls.body_fat AS body_fat_change
FROM
(SELECT ttd.person_id, bs.weight, bs.body_fat, bs.test_date
FROM body_stats AS bs
JOIN
((SELECT pbs.person_id, min(bs.test_date) AS first_test, max(bs.test_date) AS last_test
FROM body_stats AS bs,
person_body_stats AS pbs,
team_member AS tm,
team AS t
WHERE t.team_id = team_id
AND tm.team_id = t.team_id
AND tm.person_id = pbs.person_id
AND pbs.body_stats_id = bs.body_stats_id
AND tm.start_date bs.test_date
AND bs.test_date >= t.starting_date
GROUP BY person_id) AS ttd,
person_body_stats AS pbs)
ON bs.test_date = ttd.first_test
AND pbs.person_id = ttd.person_id
AND pbs.body_stats_id = bs.body_stats_id) AS fs,
(SELECT ttd.person_id, bs.weight, bs.body_fat, bs.test_date
FROM body_stats AS bs
JOIN
((SELECT pbs.person_id, min(bs.test_date) AS first_test, max(bs.test_date) AS last_test
FROM body_stats AS bs,
person_body_stats AS pbs,
team_member AS tm,
team AS t
WHERE t.team_id = team_id
AND tm.team_id = t.team_id
AND tm.person_id = pbs.person_id
AND pbs.body_stats_id = bs.body_stats_id
AND tm.start_date bs.test_date
AND bs.test_date >= t.starting_date
GROUP BY person_id) AS ttd,
person_body_stats AS pbs)
ON bs.test_date = ttd.last_test
AND pbs.person_id = ttd.person_id
AND pbs.body_stats_id = bs.body_stats_id) AS ls
WHERE ls.person_id = fs.person_id
) AS delta;
END
I don't know how easily you can get these results from 5 tables incorporated into a temporary table or a view but essentially, following should do the same as what you are currently trying to using 3 stored procedures for.
SELECT person_id
, (SELECT weight
FROM FiveTablesResult
WHERE person_id = ftr.person_id
AND test_date = (SELECT MIN(test_date)
FROM FiveTablesResult
WHERE person_id = ftr.person_id)
) AS first_weight
, (SELECT weight
FROM FiveTablesResult
WHERE person_id = ftr.person_id
AND test_date = (SELECT MAX(test_date)
FROM FiveTablesResult
WHERE person_id = ftr.person_id)
) AS last_weight
FROM FiveTablesResult ftr
Note that there is room for optimization but let us first get the correct results returned