Procedure that will use an inside output to create a view - mysql

I want to achieve the following. I have an application that uses chats and I want to display to the logged-in user his chats in which he is involved.
I have a table named:
- CHAT_USER where I store the ID of the conversation & user and
- CHAT where I have the Topic and other information.
My approach is to get the list of chat IDs by selecting in the first instance what I received from the parameters and then use that output to create a view with the active chats.
DELIMITER $$
CREATE DEFINER=`mysql`#`%` PROCEDURE `GetChats`(
IN userName VARCHAR(60),
IN ticket INT)
NOT DETERMINISTIC CONTAINS SQL SQL SECURITY DEFINER
BEGIN
SELECT
( select `CHAT_USER_ID`, `CHAT_ID`, `TICKET_ID`, `USER` from `CHAT_USER`
where `USER`= userName and `TICKET_ID` = ticket ) as ChatsForUser ,
(select `r`.`CHAT_ID` ,
`l`.`CHAT_TYPE` ,
`r`.`TICKET_ID`,
group_concat(`r`.`USER` separator ',') AS `USER`,
`l`.`IS_PUBLIC` ,
`r`.`IS_ACTIV` ,
`l`.`CHAT_TOPIC`
from (`CHAT_USER` `r` left join `CHAT` `l` on((`r`.CHAT_ID` = `l`.`CHAT_ID`)))
where ((`r`.`IS_ACTIV` = '1') and (`r`.`USER_TYPE` <> 'INITIATOR'))
group by `r`.`CHAT_ID` having `r`.`CHAT_ID` IN ChatsForUser.`CHAT_ID`) as OutputChats;
COMMIT;
END$$
DELIMITER ;
More info:
Table CHAT_USER
===========================================================
CHAT_USER_ID | CHAT_ID | TICKET_ID | USER | ...other fields
===========================================================
Table CHAT
============================================================================
CHAT_ID | TICKET_ID | CHAT_TYPE | CHAT_TOPIC | IS_ACTIV | IS_PUBLIC | other fields
============================================================================
I want an output that will have this:
===============================================================
CHAT_ID | TICKET_ID | USERS | IS_ACTIV | IS_PUBLIC | CHAT_TOPIC
===============================================================
CONDITIONS:
To display active CHAT_IDs on a TICKET, where the logged-in user is involved in conversation.
An example of output should return only the first 2 rows, because ME is involved in the discussions:
12 | 234 | me, user1, user 4 | 1 | 1 | Problem 1
14 | 234 | me, user56, user 9 | 1 | 1 | Problem 233
19 | 234 | user44, user 19 | 1 | 1 | Problem 12
22 | 234 | user33, user 22 | 1 | 1 | Problem 230
Is this possible? If not, can give some ideas how to achieve this? Thank you in advance!

You can try following Select statement before you start with your procedure and see if it returns the wanted data. The testing only is possible with actual data and the wanted result.
SELECT
c.CHAT_ID,c.TICKET_ID
,GROUP_CONCAT(DISTINCT Cu.USER ORDER BY Cu.USER SEPARATOR ', '),
MAX(cu.IS_ACTIV), MAX(cu.IS_PUBLIC),MAX(cu.CHAT_TOPIC)
FROM CHAT C inner join CHAT_USER cu
On c.CHAT_ID = cu.CHAT_ID and c.TICKET_ID =cu.TICKET_ID;
GROUP BY c.CHAT_ID,c.TICKET_ID
try this if it gives you the expected result
DELIMITER $$
CREATE DEFINER=`mysql`#`%` PROCEDURE `GetChats`(
IN userName VARCHAR(60),
IN ticket INT)
NOT DETERMINISTIC CONTAINS SQL SQL SECURITY DEFINER
BEGIN
SELECT
c.CHAT_ID,c.TICKET_ID
,GROUP_CONCAT(DISTINCT Cu.USER ORDER BY Cu.USER SEPARATOR ', '),
MAX(c.IS_ACTIV), MAX(c.IS_PUBLIC),MAX(c.CHAT_TOPIC)
FROM CHAT C
inner join (SELECT * FROM CHAT_USER WHERE `CHAT_ID`
in
(SELECT `CHAT_ID` FROM CHAT_USER WHERE `USER` Like userName )) cu
On c.CHAT_ID = cu.CHAT_ID and c.TICKET_ID =cu.TICKET_ID
WHERE c.TICKET_ID = ticket
GROUP BY c.CHAT_ID,c.TICKET_ID;
END$$
DELIMITER ;
This procedure will give you all the chats and users to a specific ticket id.
As i said in the comment. i don't completely understand your stored procedure.

Related

Update columns of a table which data contains from another table mysql

I am about to update records which contain data from another table. From the given example table data below, I want to update the NULL values of company_name and domain of log table:
User Table
id | email_address
1 test#gmail.com
2 test#yahoo.com
Log table
id | user_id | company_name | domain
1 1 NULL | NULL
2 1 NULL | NULL
3 2 Yahoo | yahoo.com
4 1 Google Inc | gmail.com
Company_domain table
id | company | domain
1 | Google Inc | google.com,gmail.com,gmail.com.us
2 | Yahoo | yahoo.com,yahoomail.com
The company_name should be based on the domain of user email address. From the example log table above, the company_name of id #3 is Yahoo since the user_id=2 which is test#yahoo.com. This should also reflect on log.domain
My sql query below does not match with the company.
UPDATE user_log AS log INNER JOIN user AS u ON u.id=log.user_id
SET log.domain = (
select (SUBSTR(u.email_address, INSTR(u.email_address, '#') + 1))
),
log.company_name = (
SELECT company FROM company_domain
WHERE find_in_set(
(
SELECT (SUBSTR(log.domain, INSTR(log.domain, '#') + 1))
),
domain
)
);
Does anybody know?
I got this sql query working on my local. I notice that the domain should come from the email address since the domain from log table is null.
UPDATE user_log ul
INNER JOIN user u ON u.id = ul.user_id
SET
ul.domain = (
SELECT (SUBSTR(u.email_address, INSTR(u.email_address, '#') + 1))
),
ul.company_name = (
SELECT company FROM company_domain
WHERE FIND_IN_SET(
(
SELECT (SUBSTR(u.email_address, INSTR(u.email_address, '#') + 1))
),
domain
) LIMIT 1
);

Update Records in Table based on query result and variable - result only returns record for first int in variable

I want to update records in table Users that are not present in table UserActions (see sqlfiddle demo or sql and data at gist.github)
My tables
Table Users
ID | UserName | isActive
1 | Ben Busy | 1
2 | Lui Lazy | 1 <-- never logged in
3 | Emmy Eager | 1
4 | Lana Later | 1 <-- never logged in
Table UserActions
ID | User_ID | Type | ActionDate
1 | 1 | Login | 2021-01-01 <-- Joe
2 | 3 | Login | 2021-01-02 <-- Eda
3 | 1 | Login | 2021-01-02 <-- Joe
4 | 1 | Login | 2021-01-03 <-- Joe
I want to set isActive = 0 for all Users that never logged in.
This query returns the userIDs that never logged in:
SELECT ID FROM Users u LEFT JOIN UserActions ua
ON u.ID = ua.User_ID
AND (ua.Type = "Login" OR ua.Type = NULL)
WHERE ua.ActionDate IS NULL
I was not able to use this question or this question so i thought that using a varible SET #userIDs := (....) should work as well
SET #userIDs := (
SELECT GROUP_CONCAT(ID SEPARATOR ', ') FROM Users u LEFT JOIN UserActions ua
ON u.ID = ua.User_ID
AND (ua.Type = "Login" OR ua.Type = NULL)
WHERE ua.ActionDate IS NULL);
The varible #userIDs contains all the relevant user-ids that never logged in (2,4).
But this statement
SELECT * FROM Users where ID in (#userIDs);
only returns the first result.
Questions
Why does where ID in #myVar not work?
Is there a way besides using a temporary table?
Code and Data
sql and data at gist
sqlfiddle demo
Perhaps you can just use NOT EXISTS:
UPDATE Users u
SET u.IsActive=0
WHERE NOT EXISTS
(SELECT user_id FROM UserActions ua
WHERE (ua.Type = "Login" OR ua.Type = NULL) AND u.ID=ua.user_id);
Demo fiddle
Update 3rd party edit
If you want to use a variable you can do it with find_in_set
SET #userIDs := (
SELECT GROUP_CONCAT(u.ID SEPARATOR ',') FROM Users u
LEFT JOIN UserActions ua
ON u.ID = ua.User_ID
AND (ua.Type = "Login" OR ua.Type = NULL)
WHERE ua.ActionDate IS NULL);
And then
SELECT * FROM Users WHERE FIND_IN_SET(Users.ID, #userIDs);
See this dbfiddle

Create trigger for several rows

I have table users AND orders. After every UPDATE row in orders. I want update DATA in users table namely concat(OLD.DATA + ID which was updated).
Table 'users'.
ID NAME DATA
1 John 1|2
2 Michael 3|4
3 Someone 5
Table 'orders'.
ID USER CONTENT
1 1 ---
2 1 ---
3 2 ---
4 2 ---
5 3 ---
For example:
SELECT `data` from `users` where `id` = 2; // Result: 3|4
UPDATE `orders` SET '...' WHERE `id` > 0;
**NEXT LOOP**
UPDATE `users` SET `data` = concat(OLD.data, ID.rowUpdated) WHERE `user` = 1;
UPDATE `users` SET `data` = concat(OLD.data, ID.rowUpdated) WHERE `user` = 1;
UPDATE `users` SET `data` = concat(OLD.data, ID.rowUpdated) WHERE `user` = 2;
UPDATE `users` SET `data` = concat(OLD.data, ID.rowUpdated) WHERE `user` = 2;
UPDATE `users` SET `data` = concat(OLD.data, ID.rowUpdated) WHERE `user` = 3;
Result:
SELECT data from users where id = 1; // Result: 1|2|1|2
SELECT data from users where id = 2; // Result: 3|4|3|4
SELECT data from users where id = 3; // Result: 5|5
How can I do it?
I think you are making the same mistake I made not too long ago, ie storing an array/object in a column.
I would recommend using the following tables in your scenario:
users
+-----------+-----------+
| id | user_name |
+-----------+-----------+
| 1 | John |
+-----------+-----------+
| 2 | Michael |
+-----------+-----------+
orders
+-----------+-----------+------------+
| id | user_id |date_ordered|
+-----------+-----------+------------+
| 1 | 1 | 2019-03-05 |
+-----------+-----------+------------+
| 2 | 2 | 2019-03-05 |
+-----------+-----------+------------+
Where user_id is the foreign key to users
sales
+-----------+-----------+------------+------------+------------+
| id | order_id | item_sku | qty | price |
+-----------+-----------+------------+------------+------------+
| 1 | 1 | 1001 | 1 | 2.50 |
+-----------+-----------+------------+------------+------------+
| 2 | 1 | 1002 | 2 | 3.00 |
+-----------+-----------+------------+------------+------------+
| 3 | 2 | 1001 | 2 | 2.00 |
+-----------+-----------+------------+------------+------------+
where order_id is the foreign key to orders
Now for the confusing part. You will need to use a series of JOINs to access the relevant data for each user.
SELECT
t3.id AS user_id,
t3.user_name,
t1.id AS order_id,
t1.date_ordered,
SUM((t2.price * t2.qty)) AS order_total
FROM orders t1
JOIN sales t2 ON (t2.order_id = t1.id)
LEFT JOIN users t3 ON (t1.user_id = t3.id)
WHERE user_id=1
GROUP BY order_id;
This will return:
+-----------+--------------+------------+------------+--------------+
| user_id | user_name | order_id |date_ordered| order_total |
+-----------+--------------+------------+------------+--------------+
| 1 | John | 1 | 2019-03-05 | 8.50 |
+-----------+--------------+------------+------------+--------------+
These type of JOIN statements should come up in basically any project using a relational database (that is, if you are designing your DB correctly). Typically I create a view for each of these complicated queries, which can then be accessed with a simple SELECT * FROM orders_view
For example:
CREATE
ALGORITHM = UNDEFINED
DEFINER = `root`#`localhost`
SQL SECURITY DEFINER
VIEW orders_view AS (
SELECT
t3.id AS user_id,
t3.user_name,
t1.id AS order_id,
t1.date_ordered,
SUM((t2.price * t2.qty)) AS order_total
FROM orders t1
JOIN sales t2 ON (t2.order_id = t1.id)
LEFT JOIN users t3 ON (t1.user_id = t3.id)
GROUP BY order_id
)
This can then be accessed by:
SELECT * FROM orders_view WHERE user_id=1;
Which would return the same results as the query above.
Depending on your needs, you will probably need to add a few more tables (addresses, products etc.) and several more rows to each of these tables. Very often you will find that you need to JOIN 5+ tables into a view, and sometimes you might need to JOIN the same table twice.
I hope this helps despite it not exactly answering your question!
It is probably a bad idea to update the USERS table after inserting into (or updating) the ORDERS table. Avoid storing data twice. In your case: you can always get all "order ids" for a user by querying the ORDERS table. Thus, you don't need to store them in the USERS table (again). Example (tested with MySQL 8.0, see dbfiddle):
Tables and data
create table users( id integer primary key, name varchar(30) ) ;
insert into users( id, name ) values
(1, 'John'),(2, 'Michael'),(3, 'Someone') ;
create table orders(
id integer primary key
, userid integer
, content varchar(3) references users (id)
);
insert into orders ( id, userid, content ) values
(101, 1, '---'),(102, 1, '---')
,(103, 2, '---'),(104, 2, '---'),(105, 3, '---') ;
Maybe a VIEW - similar to the one below - will do the trick. (Advantage: you don't need additional columns or tables.)
-- View
-- Inner SELECT: group order ids per user (table ORDERS).
-- Outer SELECT: fetch the user name (table USERS)
create or replace view userorders (
userid, username, userdata
)
as
select
U.id, U.name, O.orders_
from (
select
userid
, group_concat( id order by id separator '|' ) as orders_
from orders
group by userid
) O join users U on O.userid = U.id ;
Once the view is in place, you can just SELECT from it, and you will always get the current "userdata" eg
select * from userorders ;
-- result
userid username userdata
1 John 101|102
2 Michael 103|104
3 Someone 105
-- add some more orders
insert into orders ( id, userid, content ) values
(1000, 1, '***'),(4000, 1, '***'),(7000, 1, '***')
,(2000, 2, ':::'),(5000, 2, ':::'),(8000, 2, ':::')
,(3000, 3, '###'),(6000, 3, '###'),(9000, 3, '###') ;
select * from userorders ;
-- result
userid username userdata
1 John 101|102|1000|4000|7000
2 Michael 103|104|2000|5000|8000
3 Someone 105|3000|6000|9000

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

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