MySQL populating field based on another table using LIKE match - php

I know it's not the cleanest code to date, but I can't figure out why I can't get this one to work.
I'm looking to populate the field m.customersTemp with Customer Numbers from field c.ClientNumber. But only when a LIKE match from c.EmailAddress is found m.Emails... m.Emails is a field with a list of e-mails. Code Below.
UPDATE market m, customer c
SET m.customersTemp = CONCAT(m.customersTemp, c.ClientNumber)
WHERE m.Emails LIKE CONCAT('%', TRIM(c.EMailAddress), '%')
AND TRIM(c.EMailAddress)<>''
The result in field m.customersTemp only displays one value (customer number)... and I know there are many matches.
TABLE CUSTOMER
ClientNumber | EMailAddress
1234 a#a.com
4567 b#b.com
2222
1111 d#d.com
-------------------------------------------------------------
| TABLE MARKET |
-------------------------------------------------------------
| ID | Emails | customersTemp|
-------------------------------------------------------------
|1 | a#a.com, b#b.com, c#c.com | |
|2 | a#a.com, b#b.com, g#g.com | |
|3 | e#e.com | |
|4 | f#f.com | |
-------------------------------------------------------------
Result in customersTemp at ID 1 and 2 is only 1 ClientNumber. 4567

Don't forget to read the Warning at the bottom as to why you should NEVER save your data like this.
You can test this on a backup copy. I wouldn't run it against your main tables. Akin to someone saying: "Here, try this delete command, I think it will work."
-- drop table customer;
create table customer
( ClientNumber int,
EMailAddress varchar(100)
);
insert customer (ClientNumber,EMailAddress) values
(1234,'john#john.com'),
(4567,'joe#joe.com'),
(2222,''),
(1111,'somone#someone.com'),
(5454,'john#john.com');
-- drop table market;
create table market
( Emails varchar(100),
customersTemp varchar(100)
);
insert market(Emails,customersTemp) values
('john#john.com',''),
('joe#joe.com',''),
('test#test.com',''),
('more#more.com','');
The Update statement:
UPDATE market
INNER JOIN
( SELECT c.EMailAddress as e,GROUP_CONCAT(c.ClientNumber ORDER BY c.ClientNumber) theList
FROM customer c
GROUP BY c.EMailAddress
) xDerived1
ON market.EMails = xDerived1.e
SET market.customersTemp = xDerived1.theList;
Results:
select * from market;
+---------------+---------------+
| Emails | customersTemp |
+---------------+---------------+
| john#john.com | 1234,5454 |
| joe#joe.com | 4567 |
| test#test.com | |
| more#more.com | |
+---------------+---------------+
Version2
drop table customer;
create table customer
( ClientNumber int,
EMailAddress varchar(100)
);
insert customer (ClientNumber,EMailAddress) values
(1234,'a#a.com'),
(4567,'b#b.com'),
(2222,''),
(1111,'d#d.com'),
(8484,'g#g.com');
-- select * from customer;
drop table market;
create table market
( id int auto_increment primary key,
Emails varchar(100),
customersTemp varchar(3000)
);
insert market(Emails,customersTemp) values
('a#a.com,b#b.com,c#c.com',''),
('a#a.com,b#b.com,g#g.com',''),
('e#e.com',''),
('f#f.com','');
-- select * from market;
drop table if exists marketHelper7;
create table marketHelper7
( -- btw this might be the kind of table
-- as an intersect/junction table that you
-- should have to begin with
-- and not have your CSV stuff
cid int not null,
mid int not null
);
insert marketHelper7 (cid,mid)
select c.ClientNumber,m.id as MarketId
from customer c
join market m
on find_in_set(c.EMailAddress,m.Emails)>0;
update market set customersTemp=''; -- do a reset
UPDATE market m
join
( SELECT mh.mid as i,GROUP_CONCAT(mh.cid ORDER BY mh.cid) theList
FROM marketHelper7 mh
GROUP BY mh.mid
) xDerived1
ON m.id = xDerived1.i
SET m.customersTemp = xDerived1.theList;
drop table marketHelper7;
.
select * from market;
+----+-------------------------+----------------+
| id | Emails | customersTemp |
+----+-------------------------+----------------+
| 1 | a#a.com,b#b.com,c#c.com | 1234,4567 |
| 2 | a#a.com,b#b.com,g#g.com | 1234,4567,8484 |
| 3 | e#e.com | |
| 4 | f#f.com | |
+----+-------------------------+----------------+
Version 2 above has the helper table.
Warning:
By the way, never save your data like this. It is insane, and the performance is awful. Please see my answer here on Junction Tables (many-to-many) (similar to association tables or "item has" tables a.k.a. One-to-Many). They are all the same concept that utilized Data Normalization best practices and fast indexes during queries. Plus you stay happier not fighting with your data constantly or wondering if you blow the buffer size with group_concat.
Note that group_concat() has flexibility for its separator choice, and the order by, baked inside the function call.
The maximum length for the output of group_concat is subject to the system variable group_concat_max_len which probably defaults to 1K but can be set to at least 4GB.
The Percona article on group_concat(), and the manual pages for group_concat() and find_in_set().

Related

How SELECT separate | column mysql php (1|0|2|1|0|0|1)

I have column student_hobbys in my mysql table:
student_hobbys
1|0|1|1|0|0|1|0
now i try search all students who likes geography (school subject). In column student_hobbys i save this value in second argument (1|here|0|1|...). How to create a mysql query that will select students who like geography?
please help
Please read here why your design is a bad idea. You should store you data in a normalized way as follows:
table studens:
id | name
1 | John
2 | Jane
3 | Mike
4 | Spike
table hobbies:
id | name
1 | biology
2 | geogryphy
3 | football
4 | programming
table students_hobbies:
student_id | hobby_id
1 | 3
1 | 4
2 | 1
2 | 2
3 | 1
3 | 2
3 | 4
4 | 3
Schema definition:
CREATE TABLE students (
id INT UNSIGNED AUTO_INCREMENT,
name VARCHAR(50),
PRIMARY KEY (id),
INDEX (name)
);
CREATE TABLE hobbies (
id INT UNSIGNED AUTO_INCREMENT,
name VARCHAR(50),
PRIMARY KEY (id),
INDEX (name)
);
CREATE TABLE students_hobbies (
student_id INT UNSIGNED,
hobby_id INT UNSIGNED,
PRIMARY KEY (student_id, hobby_id),
INDEX (hobby_id, student_id)
);
And your SELECT query now would be:
SELECT s.*
FROM students s
JOIN students_hobbies sh ON sh.student_id = s.id
JOIN hobbies h ON h.id = sh.hobby_id
WHERE h.name = 'geogryphy';
Result:
| id | name |
| --- | ---- |
| 2 | Jane |
| 3 | Mike |
View on DB Fiddle
However - If you want to stick with your design, you can try something like this:
SELECT *
FROM students
WHERE student_hobbys LIKE '_|1%'
View on DB Fiddle
But it would be quite complex to generate this query programmatically. It will probably also be slower than the above solution on big dada sets, because there is no way to use an index for this kind of query.
If you want to avoid complex code in your application, you will need a quite more comlex query. One way would be to convert your string to a bitmask, and then use the bit operator & to check the bit at a specific position:
SET #hobby_position = 2;
SELECT *
FROM students
WHERE CONV(REVERSE(REPLACE(student_hobbys, '|', '')), 2, 10) & 1 << (#hobby_position - 1);
| id | name | student_hobbys |
| --- | ---- | --------------- |
| 2 | Jane | 1|1|1|1|0|0|1|0 |
| 3 | Mike | 1|1|1|1|0|0|1|0 |
View on DB Fiddle
There are other ways - But you will unlikely find a simple one, which can work with your design.

How can i get the total of a column in mysql?

Alright, I am kind of new to SQL and I have no idea on how I am going to do this. I have tried to google but maybe I'm not sure if I am searching for the right question. So here it is. I have 2 tables in my database. One is called events and another one is called eventtypes.
My eventtype table looks like this..
-------------------
eventID | eventName|
1 | eveA |
2 | eveB |
3 | eveC |
___________________
and my events table, looks like this
--------------------------
eventCat |UserRegistered |
1 | John |
2 | Mac |
3 | Assz |
2 | Ez |
3 | Pz |
_________________________
(I hope you understand my table.....)
events.eventCat=eventtype.eventID
Now, what I am trying to do is to, calculate the numbers of participants for event 1, 2 and 3 in events table and later display the number of users who will be attending appropriately by using the eventtype table using php.
Can somebody help me out with this ? Thanks a bunch !
create table eventtype
( eventID int not null,
eventName varchar(100) not null
);
insert eventtype(eventID,eventName) values
(1,'eveA'),
(2,'eveB'),
(3,'eveC');
create table events
( eventCat int not null,
UserRegistered varchar(100) not null
);
insert events(eventCat,UserRegistered) values
(1,'John'),
(2,'Mac'),
(3,'Assz'),
(2,'Ez'),
(3,'Pz');
Query:
select e.eventId,count(u.UserRegistered) as theCount
from eventtype e
join events u
on u.eventCat=e.eventId
group by e.eventId
order by e.eventId;
+---------+----------+
| eventId | theCount |
+---------+----------+
| 1 | 1 |
| 2 | 2 |
| 3 | 2 |
+---------+----------+
You can apply join and group by to acheive this -
SELECT count(UserRegistered),eventName from events join eventtype on
events.eventCat=eventtype.eventID
GROUP BY eventCat
Hope this will help you.
The followng query should work.it will select the no of participants based on the event using group by clause
SELECT count(UserRegistered),eventName
From events,eventype
WHERE events.eventCat=eventtype.eventID
GROUP BY eventCat

Insert data using INSERT INTO command.

I have two table name users and users_images. Both table have the value of userId. like
My user table
| userId | userName | user_address |
| 2 | John | CN-2, UK |
| 3 | Amit | India |
| 4 | David | Us |
| 5 | Shan | Canada |
.
.
...... and so on
| 125000 | Naved | Ukran |
**and my images table contain userid and Image name.
Now I want to merge ImageName field to user table without using any loop (I want to do it with single query (I have millions of records and I will have to do it many times to create temorary table) )
update users u
set
u.imageName = (
select imageName
from users_images i
where i.userid = u.userid GROUP BY u.userId )
you could use ON DUPLICATE KEY
for instance:
INSERT INTO table (a,b,c) VALUES (1,2,3)
ON DUPLICATE KEY UPDATE c=c+1;
I think you can use Update for this like:
UPDATE Users
SET ImageName =
(SELECT ImageName
FROM UserImages
WHERE UserImages.UserID = Uers.UserID)
Please take a backup of your database first

JOIN 2 Tables with different columns

I Have 2 Tables, One For New Pictures and One For New Users, i want to create like a wall that mixes the latest actions so it'll show new users & pictures ordered by date.
What i want is a single query and how to know inside the loop that the current entry is a photo or user.
TABLE: users
Columns: id,username,fullname,country,date
TABLE: photos
Columns: id,picurl,author,date
Desired Output:
Daniel from California Has just registred 5mins ago
New Picture By David ( click to view ) 15mins ago
And so on...
I'm begging you to not just give me the query syntax, i'm not pro and can't figure out how to deal with that inside the loop ( i only know how to fetch regular sql queries )
Thanks
You could use an union:
SELECT concat(username, " from ", country, " has just registered") txt, date FROM users
UNION
SELECT concat("New picture By ", username, " (click to view)") txt, date FROM photos INNER JOIN users ON author=users.id
ORDER BY date DESC
LIMIT 10
This assumes that author column in photos corresponds to the users table id. If author actually is a string containing the user name (which is a bad design), you'll have to do this instead:
SELECT concat(username, " from ", country, " has just registered") txt, date FROM users
UNION
SELECT concat("New picture By ", author, " (click to view)") txt, date FROM photos
ORDER BY date DESC
LIMIT 10
Make sure you have an index on date in both tables, or this will be very inefficient.
I've put together this little example for you to look at - you might find it helpful.
Full script can be found here : http://pastie.org/1279954
So it starts with 3 simple tables countries, users and user_photos.
Tables
Note: i've only included the minimum number of columns for this demo to work !
drop table if exists countries;
create table countries
(
country_id tinyint unsigned not null auto_increment primary key,
iso_code varchar(3) unique not null,
name varchar(255) unique not null
)
engine=innodb;
drop table if exists users;
create table users
(
user_id int unsigned not null auto_increment primary key,
country_id tinyint unsigned not null,
username varbinary(32) unique not null
-- all other detail omitted
)
engine=innodb;
drop table if exists user_photos;
create table user_photos
(
photo_id int unsigned not null auto_increment primary key,
user_id int unsigned not null,
-- all other detail omitted
key (user_id)
)
engine=innodb;
The important thing to note is that the primary keys of users and photos are unsigned integers and auto_increment (1,2,3..n) so I can find the latest 10 users and 10 photos by ordering by their primary keys (PK) descending and add a limit clause to restrict the number of rows returned.
-- change limit to increase rows returned
select * from users order by user_id desc limit 2;
select * from user_photos order by photo_id desc limit 2;
Test Data
insert into countries (iso_code, name) values ('GB','Great Britain'),('US','United States'),('DE','Germany');
insert into users (username, country_id) values ('f00',1),('bar',2),('stack',1),('overflow',3);
insert into user_photos (user_id) values (1),(1),(2),(3),(1),(4),(2),(1),(4),(2),(1);
So now we need a convenient way (single call) of selecting the latest 10 users and photos. The two tables are completely different so a union isnt going to be the best approach so what we'll do instead is write a stored procedure that returns two resultsets and handle generating the wall (merge resultsets) in our php script.
Stored procedure
Just a wrapper around some SQL code - think of it like SQL's version of a function call
drop procedure if exists list_latest_users_and_photos;
delimiter #
create procedure list_latest_users_and_photos()
begin
-- last 10 users
select
'U' as type_id, -- integer might be better
u.user_id,
u.country_id,
u.username,
-- other user columns...
c.name as country_name
from
users u
inner join countries c on u.country_id = c.country_id
order by
u.user_id desc limit 10;
-- last 10 photos
select
'P' as type_id,
up.photo_id,
up.user_id,
-- other photo columns...
u.username
-- other user columns...
from
user_photos up
inner join users u on up.user_id = u.user_id
order by
up.photo_id desc limit 10;
end #
delimiter ;
Testing
To test our stored procedure all we need to do is call it and look at the results.
mysql> call list_latest_users_and_photos();
+---------+---------+------------+----------+---------------+
| type_id | user_id | country_id | username | country_name |
+---------+---------+------------+----------+---------------+
| U | 4 | 3 | overflow | Germany |
| U | 3 | 1 | stack | Great Britain |
| U | 2 | 2 | bar | United States |
| U | 1 | 1 | f00 | Great Britain |
+---------+---------+------------+----------+---------------+
4 rows in set (0.00 sec)
+---------+----------+---------+----------+
| type_id | photo_id | user_id | username |
+---------+----------+---------+----------+
| P | 11 | 1 | f00 |
| P | 10 | 2 | bar |
| P | 9 | 4 | overflow |
| P | 8 | 1 | f00 |
| P | 7 | 2 | bar |
| P | 6 | 4 | overflow |
| P | 5 | 1 | f00 |
| P | 4 | 3 | stack |
| P | 3 | 2 | bar |
| P | 2 | 1 | f00 |
+---------+----------+---------+----------+
10 rows in set (0.01 sec)
Query OK, 0 rows affected (0.01 sec)
Now we know that works we can call it from php and generate the wall.
PHP Script
<?php
$conn = new Mysqli("localhost", "foo_dbo", "pass", "foo_db");
$result = $conn->query("call list_latest_users_and_photos()");
$users = array();
while($row = $result->fetch_assoc()) $users[] = $row;
$conn->next_result();
$result = $conn->use_result();
$photos = array();
while($row = $result->fetch_assoc()) $photos[] = $row;
$result->close();
$conn->close();
$wall = array_merge($users, $photos);
echo "<pre>", print_r($wall), "</pre>";
?>
Hope you find some of this helpful :)

How to determine order for new item?

I have a members table in MySQL
CREATE TABLE `members` (
`id` int(10) unsigned NOT NULL auto_increment,
`name` varchar(65) collate utf8_unicode_ci NOT NULL,
`order` tinyint(3) unsigned NOT NULL default '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
And I would like to let users order the members how they like.
I'm storing the order in order column.
I'm wondering how to insert new user to be added to the bottom of the list.
This is what I have today:
$db->query('insert into members VALUES (0, "new member", 0)');
$lastId = $db->lastInsertId();
$maxOrder = $db->fetchAll('select MAX(`order`) max_order FROM members');
$db->query('update members
SET
`order` = ?
WHERE
id = ?',
array(
$maxOrder[0]['max_order'] + 1,
$lastId
));
But that's not really precise while when there are several users adding new members at the same time, it might happen the MAX(order) will return the same values.
How do you handle such cases?
You can do the SELECT as part of the INSERT, such as:
INSERT INTO members SELECT 0, "new member", max(`order`)+1 FROM members;
Keep in mind that you are going to want to have an index on the order column to make the SELECT part optimized.
In addition, you might want to reconsider the tinyint for order, unless you only expect to only have 255 orders ever.
Also order is a reserved word and you will always need to write it as `order`, so you might consider renaming that column as well.
Since you already automatically increment the id for each new member, you can order by id.
I am not sure I understand. If each user wants a different order how will you store individual user preferences in one single field in the "members" table?
Usually you just let users to order based on the natural order of the fields. What is the purpose of the order field?
Usually I make all my select statements order by "order, name"; Then I always insert the same value for Order (either 0 or 9999999 depending on if I want them first or last). Then the user can reorder however they like.
InnoDB supports transactions. Before the insert do a 'begin' statement and when your finished do a commit. See this article for an explanation of transactions in mySql.
What you could do is create a table with keys (member_id,position) that maps to another member_id. Then you can store the ordering in that table separate from the member list itself. (Each member retains their own list ordering, which is what I assume you want...?)
Supposing that you have a member table like this:
+-----------+--------------+
| member_id | name |
+-----------+--------------+
| 1 | John Smith |
| 2 | John Doe |
| 3 | John Johnson |
| 4 | Sue Someone |
+-----------+--------------+
Then, you could have an ordering table like this:
+---------------+----------+-----------------+
| member_id_key | position | member_id_value |
+---------------+----------+-----------------+
| 1 | 1 | 4 |
| 1 | 2 | 1 |
| 1 | 3 | 3 |
| 1 | 4 | 2 |
| 2 | 2 | 1 |
| 2 | 3 | 2 |
+---------------+----------+-----------------+
You can select the member list given the stored order by using an inner join. For example:
SELECT name
FROM members inner join orderings
ON members.member_id = orderings.member_id_value
WHERE orderings.member_id_key = <ID for member you want to lookup>
ORDER BY position;
As an example, the result of running this query for John Smith's list (ie, WHERE member_id_key = 1) would be:
+--------------+
| name |
+--------------+
| Sue Someone |
| John Smith |
| John Johnson |
| John Doe |
+--------------+
You can calculate position for adding to the bottom of the list by adding one to the max position value for a given id.

Categories