I' m trying to write a stored procedure that will search a fairly simple database with
a USER table (user_id,name,...)
a USER_TYPE table (user_id,type_id) - multi to multi
a TYPE table (type_id,type_name)
a USER_GAME (user_id,game_id) -multi to multi
a GAME table (game_id,game_name)
A same user can have several games. Now, I want to be able to get the user according to a particular type and also according to a/some particular game(s), so that for example I can get all the user with, say type1, and with the games, say game2 and game5. I think I can get round the problem of several game names by passing them as a string parameter and do some kind of HAVING LIKE condition (I call get_user_spec('type1' , 'game3,game5') for example).
So far I get to that point:
CREATE DEFINER=`root`#`localhost` PROCEDURE `get_user_spec`(
IN inTypeName VARCHAR(50),
IN inGameName VARCHAR(150)
)
BEGIN
PREPARE statement FROM
"SELECT u.user_id,t.type_name,GROUP_CONCAT(g.game_name) AS game
FROM user u
INNER JOIN user_type ut
ON u.user_id=ut.user_id
INNER JOIN type t
ON ut.type_id=t.type_id
LEFT JOIN user_game ug
ON u.user_id=ug.user_id
LEFT JOIN game g
ON ug.game_id=g.game_id
WHERE t.type_name=?
GROUP BY u.user_id
HAVING game LIKE CONCAT('%',?,'%')
ORDER BY u.user_id";
SET #p1=inTypeName;
SET #p2=inGameName;
EXECUTE statement USING #p1,#p2;
END
But my real problem is that if I don't pass any game name, I then want to get all users with type1 (I then call get_user_spec('type1' , NULL). But I am then not getting anything as the procedure sees
HAVING game LIKE CONCAT('%',NULL,'%').
I hope that was clear enough. If anybody has any suggestions to get around that problem, I would be very grateful.
Thank you very much.
Change this line:
EXECUTE statement USING #p1,#p2;
to
EXECUTE statement USING #p1, ifnull(#p2, '');
This will cause the LIKE expression to be just '%%', which means "match everything"
Related
So some background I am setting up a cloudformation stack from our CICD pipeline. This stack sets up our entire database. We have migration files that get triggered, great! So far this is working well, my db builds all my views and tables, except it does not build my procedures and I've been banging my head against my desk trying to figure out why. I've looked at a ton of documentation, I really don't understand why what I have is not working when it looks EXACTLY as it should from the documentation and several other similar sites I found.
Now I believe I know the cause of the issue, I am currently using SQLPro which generates a SQL dump of my DB, which is used in my cicd process and gets triggered. I've ran the dump file in SQLPro and it works fine, but does not work when just trying to execute from an sql file, not in SQLpro, but in our cicd pipeline.
I feel like this must be a really simple problem, I've tried every flavor I can think of trying to format it correctly and I really have no idea why it's not working. I've been using an SQL validator online, and if I put ONE stored procedure it says the syntax is fine, but when I have more than 1 it's not.
Here's 2 procedures as an example
CREATE PROCEDURE cropsByGrowerIdProc(IN userID Text)
BEGIN
SELECT DISTINCT
g.growerID AS growerID,
g.firstName AS firstName,
g.lastName AS lastName,
z.zoneID AS zoneID,
g.farmName AS farmName,
g.email AS email,
g.phoneNumber AS phoneNumber,
u.userID AS userID,
g.source AS source,
json_object('cropID',c.cropID,'plantedAcreage',c.acres) AS crops
FROM GrowerDICT g
inner join UserGrowerTransaction u on (u.growerID = g.growerID AND u.userID = userID)
left join CropTransaction c on (c.growerID = g.growerID)
inner join ZoneTransaction z on(z.growerID = g.growerID)
where (u.userID = userID AND g.isActive IS TRUE);
END;
CREATE PROCEDURE cropsByGrowerIdProc_OLD(IN userID Text)
BEGIN
SELECT DISTINCT
g.growerID AS growerID,
g.firstName AS firstName,
g.lastName AS lastName,
z.zoneID AS zoneID,
g.farmName AS farmName,
g.email AS email,
g.phoneNumber AS phoneNumber,
u.userID AS userID,
g.source AS source,
json_object('cropID',c.cropID,'plantedAcreage',c.acres) AS crops
FROM GrowerDICT g
inner join UserGrowerTransaction u on (u.growerID = g.growerID)
left join CropTransaction c on (c.growerID = g.growerID)
inner join ZoneTransaction z on(z.growerID = g.growerID)
where (u.userID = userID AND g.isActive IS TRUE AND c.isActive IS TRUE);
END;
Yes, I've tried DELIMITER's in any spot I can it just always says you have an error in your sql syntax near CREATE PROCEDURE cropsByGrowerIdProc_OLD
So again, this is just what I have now, I have tried many other things like delimiters, removing the begin/end, removing the drop procedure if exists (couldnt get that to work either, and errored out on the first create stored procedure line instead of the second without the drop procedure calls)
I have created 2 tables. One contains 5 golf courses (name & address). the other is the par and yards for all 18 holes per golf course (foreign key to link to courses). I have created a function getYards to return the number of yards for a course. I have created a 2nd function, getPar, to return the total par for a course. These each work. I now need to create a view to display the course name and the par and yardage using the 2 functions.
getYards function:
DELIMITER $$
CREATE FUNCTION getYards (id INT)
RETURNS INT (5)
BEGIN
DECLARE totalyards INT;
SELECT SUM(hole_yards) INTO totalyards FROM golf_holes WHERE id=golf_id;
RETURN totalyards;
END$$
DELIMITER ;
The function for getPar is very similar using hole_par instead of yards.
So now I want to create a view with course name, par and yardage using the getPar and getYards functions. I have tried searching and I'm just not knowledgeable enough to understand. I know this is vague, but can anyone help me ... keep it simple! thanx in advance.
I've been a cobol programmer for almost 40 years. I am trying to learn MySQL so have very little knowledge with SQL.
If you want to keep it simple, I would not recommend using stored functions in your view definition.
Instead you can just join the two tables.
Something like this should work for you:
create or replace view v_golf_courses as
select golf_courses.id as golf_course_id,
golf_courses.name as golf_course_name,
sum(golf_holes.yards) as course_yardage,
sum(golf_holes.par) as course_par
from golf_courses
inner join golf_holes on golf_holes.golf_course_id = golf_courses.id
group by golf_courses.id, golf_courses.name;
Assuming the id column in your courses table is also named golf_id
SELECT name, getPar(golf_id) as par, getYards(golf_id) as yards
FROM courses
However you don't really need UDF's for this:
SELECT a.name, sum(b.hole_par) as total_par, sum(b.hole_yards) as total_yards
FROM courses a
JOIN golf_holes b
ON a.golf_id = b.golf_id
GROUP BY a.golf_id
This is my first time creating a MySQL stored procedure and am stuck on getting the UPDATE piece to work correctly. The proc is performing an inner join, looking for matches on a domain name field. If there is a match, a column named inbound is getting updated with a value of 0. If there is not a match on the join, then I need inbound set to a value of 1.
When I run this, I am able to get the matches tagged with a 0, but the non-matches are not getting updated with a 1. I thought how I have the 'ELSE' part set up would take care of this- can anyone tell if I am missing something with the syntax?
CREATE PROCEDURE `sp_InboungTagging`()
BEGIN
update `tableA` a
inner join `TableD` d
on a.senderDomain = d.domainName
set inbound = CASE
when a.senderDomain = d.domainName then 0
ELSE 1
END
WHERE inbound is null;
END;|
DELIMITER ;
Thanks,
Ron
EDIT-
Thanks for your reply. I am looking for exact matches on a varchar field that has domain names in it- the master list of domains is in table D. If the record in TableA has a match in TableD, I want to tag that recored with a 0. If there is no match in TableD, then I would like to tag it with a 1. Let me know if that clears things up- thanks
Your JOIN condition is the same as your CASE condition. If you JOIN your two tables on:
a.senderDomain = d.domainName
Then there will be no values in the result set for which
a.senderDomain != d.domainName
so the ELSE clause of your CASE statement never fires.
Without knowing more about what you mean by "matches" and "non-matches," I can't really suggest a correction.
dont blame for the database design.I am not its database architect. I am the one who has to use it in current situation
I hope this will be understandable.
I have 3 tables containing following data with no foreign key relationship b/w them:
groups
groupId groupName
1 Admin
2 Editor
3 Subscriber
preveleges
groupId roles
1 1,2
2 2,3
3 1
roles
roleId roleTitle
1 add
2 edit
Query:
SELECT roles
from groups
LEFT JOIN preveleges ON (groups.groupId=preveleges.groupId)
returns specific result i.e roles.
Problem: I wanted to show roleTitle instead of roles in the above query.
I am confused how to relate table roles with this query and returns required result
I know it is feasible with coding but i want in SQL.Any suggestion will be appreciated.
SELECT g.groupName,
GROUP_CONCAT(r.roleTitle
ORDER BY FIND_IN_SET(r.roleId, p.roles))
AS RoleTitles
FROM groups AS g
LEFT JOIN preveleges AS p
ON g.groupId = p.groupId
LEFT JOIN roles AS r
ON FIND_IN_SET(r.roleId, p.roles)
GROUP BY g.groupName ;
Tested at: SQL-FIDDLE
I would change the data structure it self. Since It's not normalised, there are multiple elements in a single column.
But it is possible with SQL, if for some (valid) reason you can't change the DB.
A simple "static" solution:
SELECT REPLACE(REPLACE(roles, '1', 'add'), '2', 'edit') from groups
LEFT JOIN preveleges ON(groups.groupId=preveleges.groupId)
A more complex but still ugly solution:
CREATE FUNCTION ReplaceRoleIDWithName (#StringIds VARCHAR(50))
RETURNS VARCHAR(50)
AS
BEGIN
DECLARE #RoleNames VARCHAR(50)
SET #RoleNames = #StringIds
SELECT #RoleNames = REPLACE(#RoleNames, CAST(RoleId AS VARCHAR(50)), roleTitle)
FROM roles
RETURN #RoleNames
END
And then use the function in the query
SELECT ReplaceRoleIDWithName(roles) from groups
LEFT JOIN preveleges ON(groups.groupId=preveleges.groupId)
It is possible without function, but this is more readable. Made without editor so it's not tested in anyway.
You also tagged the question with PostgreSQL and it's actually quite easy with Postgres to work around this broken design:
SELECT grp.groupname, r.roletitle
FROM groups grp
join (
select groupid, cast(regexp_split_to_table(roles, ',') as integer) as role_id
from privileges
) as privs on privs.groupid = grp.groupid
join roles r on r.roleid = privs.role_id;
SQLFiddle: http://sqlfiddle.com/#!12/5e87b/1
(Note that I changed the incorrectly spelled name preveleges to the correct spelling privileges)
But you should really, really re-design your data model!
Fixing your design also enables you to define foreign key constraints and validate the input. In your current model, the application would probably break (just as my query would), if someone inserted the value 'one,two,three' into the roles table.
Edit
To complete the picture, using Postgres's array handling the above could be slightly simplified using a similar approach as MySQL's find_in_set()
select grp.groupname, r.roletitle
from groups grp
join privileges privs on grp.groupid = privs.groupid
join roles r on r.roleid::text = any (string_to_array(privs.roles, ','))
In both cases if all role titles should be shown as a comma separated list, the string_agg() function could be used (which is equivalent to MySQL's group_concat()
select grp.groupname, string_agg(r.roletitle, ',')
from groups grp
join privileges privs on grp.groupid = privs.groupid
join roles r on r.roleid::text = any (string_to_array(privs.roles, ','))
group by grp.groupname
I've to execute a complex query, selecting several columns from 7-8 tables.
We don't want to write that query in programming language (PHP - Symfony 1.4/Propel 1.4 in our case) but to create a view or stored procedure to have very simple select query for developers. I'm confused what will be better approach.
We need query in following format:
SET #PlayerId = 1;
SELECT CASE WHEN mat.player1id = #PlayerId THEN mat.player2id ELSE mat.player1id END as opponent
/*plus many other columns*/
FROM `wzo_matches` as mat /*plus few other tables*/
WHERE (mat.player1id =#PlayerId OR mat.player2id=#PlayerId)
/*plus other join conditions*/
Problem with view is, SET #PlayerId=xx statement. We don't know player id in advance but will be passed through PHP. I hope this is the reason to rule out views; is there any workaround for that?
Other option will be stored procedure. Only issue with that is, it will create a new view for every query so operation will be very heavy for DB.
Can someone suggest best approach so that developers can get required data from above query without writing above complex query in PHP. (Obviously through SP or view & simple select query from there)
Based on reply of Can I create view with parameter in MySQL?, My issue is fixed with following queries:
create function getPlayer() returns INTEGER DETERMINISTIC NO SQL return #getPlayer;
create view getPlay as
SELECT
CASE WHEN play.hiderid = getPlayer() THEN play.seekerid ELSE play.hiderid END AS opponent, play . *
FROM odd_play play, odd_match mat
WHERE (seekerid = getPlayer() OR hiderid = getPlayer())
AND play.id = mat.latestplay;
select play.*
from (select #getPlayer:=1 p) ply, getPlay play;
CREATE PROCEDURE SELECT_PLAYER(p INT) SET #PlayerId = p
SELECT CASE WHEN mat.player1id = #PlayerId THEN mat.player2id ELSE mat.player1id END as opponent
/*plus many other columns*/
FROM `wzo_matches` as mat /*plus few other tables*/
WHERE (mat.player1id =#PlayerId OR mat.player2id=#PlayerId)
/*plus other join conditions*/