using regex in mysql based on middle of string value - mysql

I have a system that uses personalized URLs (i.e. JohnSmith.MyWebsite.com). In my database, these values are stored in the "purl" column.
If six months from now, I get another john smith I need to put into my system, I simply add a 1 to his name so that his purl becomes JohnSmith1.MyWebsite.com.
My database has grown so large that checking for this manually is a real time consumer. So, I'd like to make a quick app where I can enter in names, then check against the database to return the number I should add onto the end.
How can I use mysql to search if JohnSmith[ANY NUMBER].MyWebsite.com exists while not getting a positive hit on a purl like JohnSmithson1234.MyWebsite.com?
So basically, I need an exact match on the name, and domain, but need to get the latest number used so I can add 1 to it.

You could add additional field to your database with the number of times each subdomain is created
for example
JohnSmith.MyWebsite.com - 5
This would mean that you have to create JohnSmith6.MyWebsite.com, and after you create it, update the field to
JohnSmith.MyWebsite.com - 6
Or you can do 'order by purl DESC' like other users suggested, but if you use this method, add index to the purl field.

Sql Server does allow [0-9] to match one digit from 0-9. You might want to use
johnsmith[0-9]%.MyWebsite.com
to allow for more digits (though this would also match something like johnsmith123fooledyou.MyWebsite.com)

MySQL doesn't do regex searches like you're asking. However, you can easily do this in the application logic. Do something like this:
SELECT * FROM table WHERE purl LIKE 'JohnSmith%';
Then loop over the results in your app and see if you have anything with numbers the purl column.
Also, you would be well served to downcase everything in the purl column since DNS is case insensitive and MySQL is not. You may have times where johnSmith is being searched for but JohnSmith is in the DB and you will have no results.
EDIT:
Apparently MySQL does allow regex searches. To get the one with the highest number add an "ORDER BY purl DESC LIMIT 1"

Related

How to create a table of wildcards

I have a table called blacklisted_usernames. These are usernames with wildcards in them that aren't allowed to be registered onto my site.
create table blacklisted_usernames (
name varchar(64) not null
);
Some dummy data:
insert into blacklisted_usernames (name) values
('%admin%'),
('king%'),
('bad'),
('%cool');
The % indicates the same thing as the wildcard in the LIKE function in MySQL. I want to create an efficient case insensitive query which tells me if a username is blacklisted or not. For example is the username AdminJohn allowed? The answer would be no, because of %admin% being in the blacklisted_usernames table.
I understand I can do something like
SELECT 1
WHERE 'AdminJohn' LIKE '%admin%'
or 'AdminJohn' LIKE 'king%'
or 'AdminJohn' LIKE 'bad'
or 'AdminJohn' LIKE '%cool'
But I am manually typing out all the LIKE's. I also don't think it would be efficient if I created a loop checking it 1 by 1. Is there a way I can make this into an automatic but efficient way of checking against the names in blacklisted_usernames table and determining if a username is allowed?
select 1 from blacklisted_usernames where 'AdminJohn' like name limit 1
Another approach is to use a REGEXP to test them all in a single test:
WHERE 'AdminJohn' REGEXP 'admin|^king|^bad$|cool$'
(Note how the wildcards go away on some and "anchors" are needed on the others.)
Probably this is faster than the original OR+LIKEs or the JOIN+LIKEs. When checking one name REGEXP will be plenty fast (no table scan).
If you need to check all existing names against the blacklisted patterns
SELECT usernames.name,
blacklisted_usernames.name blacklisted_pattern
FROM usernames
JOIN blacklisted_usernames
ON usernames.name LIKE blacklisted_usernames.name
Pay attention - this is complete tablescan, the indices won't be used, so the query will be slow.
You may need to check all existing names against newly added/altered patterns - in this case add according WHERE by blacklisted_usernames table (for example, by created_at or updated_at column).
If you need to check currently created username against all patterns then use the solution provided by ysth.

Switch values in MySQL table

I have a table in MySQL with a field "Ordering" These are just auto incremented numbers. Now I wonder if there is a query to change the values from the last to the first...
So the entry with ordering 205 should become 1, 204 -> 2 and so on...
It's actually not an auto-increment. The problem is I started adding projects from the current website. From page 1 to page 20, but the first item on page 1 is the latest. The way I added the new projects, the newest is on the last page..
If the ordering field is switched, the new items added will be correctly numbered again and added to the front page. It's just a wrong way I started adding old projects...
Structure
Examples of the content
I can't comment due to limitations, but i really agree with #Abhik Chakraborty.
You don't want to do this. Just use the order by as he suggested.
Example:
SELECT * FROM tableName
ORDER BY columnName DESC;
Just in case you would like to know more about it: http://www.w3schools.com/sql/sql_orderby.asp
Try this as one statement call:
SET #MaxSort := (SELECT MAX(Ordering) FROM MyTable);
UPDATE MyTable t set t.Ordering = (#MaxSort + 1 - t.Ordering);
This will work if field doesn't have unique constraint.
But this field, should not be an auto_increment field at first place. Auto increment is increasing NOT decreasing counter. Except if you just try to fix existing data and the new records will be increasing.
Additional explanation
Thanks for pointing it out. Multiple query inside single query statement doesn't work with php_mysqli and it is not used because of potential MySQL injection attack if servers allows it. Maybe you can setup PHPMyAdmin to use PHP PDO.
I can use multiple queries, but I'm using PHP PDO or DBeaver database manager.
I can only suggest to supply MaxSort manually (since this is one time job anyway):
UPDATE
MyTable t
set
t.Ordering = 254 - t.Ordering + 1;

Getting the highest number from a mysql query

My issue is I need to create an order so I can move items up and down in this web application. However, I can not index the order(ord column) values by an index with an incremental value because there are several companies in the same table that use this column.
My table structure is this:
Right now I am thinking that the easiest way would be to do a MAX and grab the highest number and use that as an index in a way so you never end up with the same number twice for a specific companies listing when you go to add a new entry for a company.
SELECT MAX(ord) FROM phonebook WHERE `id_company` = "51";
Would this be a wise route to go? OR maybe create a new database for each client and create and index and use that as a way order entries?
I suggest you aim for less than complete perfection in your assignment of ord values. You can get away with this as follows:
don't make ord unique. (It isn't).
rely on the ordering of phonebook_name to get a good order of names. MySQL has these wonderful case-insensitive collations for precisely this purpose.
I suppose you're trying to make some of the entries for a company come first, and others come last. Set the ord column to 50 for everybody, then give the entries you want first lower numbers, and the ones you want last higher numbers.
When you display data for a particular company, do it like this ...
SELECT whatever, whatever
FROM phonebook
WHERE id_company = 11
ORDER BY ord, phonebook_name, phonebook_number, id_phonebook
This ORDER BY clause will do what you want, and it will be stable if there are duplicates. You can then, in your user interface, move an entry up with a query like this.
UPDATE phonebook SET ord=ord-1 WHERE id_phonebook = :recordnumber

MySQL 5.5 Database design. Problem with friendly URLs approach

I have a maybe stupid question but I need to ask it :-)
My Friendly URL (furl) database design approach is fairly summarized in the following diagram (InnoDB at MySQL 5.5 used)
Each item will generate as many furls as languages available on the website. The furl_redirect table represents the controller path for each item. I show you an example:
item.id = 1000
item.title = 'Example title'
furl_redirect = 'item/1000'
furl.url = 'en/example-title-1000'
furl.url = 'es/example-title-1000'
furl.url = 'it/example-title-1000'
When you insert a new item, its furl_redirect and furls must be also inserted. The problem appears becouse of the (necessary) unique constraint in the furl table. As you see above, in order to get unique urls, I use the title of the item (it is not necessarily unique) + the id to create the unique url. That means the order of inserting rows should be as follow:
1. Insert item -- (and get the id of the new item inserted) ERROR!! furl_redirect_id must not be null!!
2. Insert furl_redirect -- (need the item id to create de path)
3. Insert furl -- (need the item id to create de url)
I would like an elegant solution to this problem, but I can not get it!
Is there a way of getting the next AutoIncrement value on an InnoDB Table?, and is it recommended to use it?
Can you think of another way to ensure the uniqueness of the friendly urls that is independent of the items' id? Am I missing something crucial?
Any solution is welcome!
Thanks!
You can get an auto-increment in InnoDB, see here. Whether you should use it or not depends on what kind of throughput you need and can achieve. Any auto-increment/identity type column, when used as a primary key, can create a "hot spot" which can limit performance.
Another option would be to use an alphanumeric ID, like bit.ly or other URL shorteners. The advantage of these is that you can have short IDs that use base 36 (a-z+0-9) instead of base 10. Why is this important? Because you can use a random number generator to pick a number out of a fairly big domain - 6 characters gets you 2 billion combinations. You convert the number to base 36, and then check to see if you already have this number assigned. If not, you have your new ID and off you go, otherwise generate a new random number. This helps to avoid hotspots if that turns out to be necessary for your system. Auto-increment is easier and I'd try that first to see if it works under the loads that you're anticipating.
You could also use the base 36 ID and the auto-increment together so that your friendly URLs are shorter, which is often the point.
You might consider another ways to deal with your project.
At first, you are using "en/" "de/" etc, for changing language. May I ask how does it work in script? If you have different folders for different languages your script and users must suffer a lot. Try to use gettext or any other localisation method (depends on size of your project).
About the friendly url's. My favorite method is to have only one extra column in item's table. For example:
Table picture
id, path, title, alias, created
Values:
1, uploads/pics/mypicture.jpg, Great holidays, great-holidays, 2011-11-11 11:11:11
2, uploads/pics/anotherpic.jpg, Great holidays, great-holidays-1, 2011-12-12 12:12:12
Now in the script, while inserting the item, create alias from title, check if the alias exists already and if does, you can add id, random number, or count (depending on how many same titles u have already).
After you store the alais like this its very simple. User try to access
http://www.mywebsite.com/picture/great-holidays
So in your script you just see that user want to see picture, and picture with alias great-holidays. Find it in DB and show it.

How can I pull an ID from a varchar field and JOIN another table?

I have a field called 'click_target' that stores a string of data similar to this:
http://domain.com/deals/244?utm_source=sendgrid.com&utm_medium=email&utm_campaign=website
Using a MySQL query, is it possible to pull the ID (244) from the string and use it to join another table?
You can certainly play games with expressions to pull the ID out of this string but I have a bigger worry - you're burning a dependency on the URL format into a query in the database. That's not really a good idea becuase when (I don't say IF) the URL's change your queries will suddenly fail silently - no errors, just empty (if you're lucky) or nonsensical results.
It's an ugly hack, but I believe this line will extract your ID for you in the above url.
REVERSE(LEFT(LOCATE('/', REVERSE(LEFT(click_target, LOCATE('?', click_target)-1)))-1))
Basically, I am getting the text between the first '?' and the last '/'. From there you can join on whatever table you want with that value, though I recommend aliasing it or storing it in a variable so that it is not recalculated frequently.
If you need the id, fix your database to store it porperly in a separate field.