I created a table for my cellphone contacts and I want to sort the cellphone numbers. Example if the phone number starts with 1 to 3 it belongs to the first telecommunications company and 4 to 6 belongs to other.
What I want to do is if the user enters their phone number the number will be inserted to the table of the telecom company but each telecom company is dependent on the ContactId which is the primary key in cellphoneContacts table.
I already tried using cellnum1 but nothing works
cellnum1 ENUM('1%','2%','3%') .............
cellnum1 VARCHAR(11) CHECK (cellnum1 IN ('1%','2%','3%')) .........
cellnum1 VARCHAR(11) CHECK (cellnum1 LIKE('1%','2%','3%'))
SELECT * FROM tbl_item WHERE SUBSTR([<Column name>], 1, X) = '<Your ID prefix>' ORDER BY [<Column name>]
Where X from SUBSTR is howmany characters do you want.
And please, be more specific with what you want in the future. :)
The MySQL Reference Manual says:
The CHECK clause is parsed but ignored by all storage engines.
Try a trigger...
If you are checking first character value , you can use substr on the NEW table rows to filter data :
substr(NEW.cellnum1 ,1,1) in ( 1,2,3)
I have a stocks table (for products/stocks of a retail store) and a serials table (barcodes issued for each stock).
Basically when new stocks are introduced to the databases, the system issues a serial number for each stock... based on the index/pri autoincrement value of the serials table.
Problem is they both depend on each other...
I'll explain:
STOCKS TABLE
stock_id int(11)
product_name varchar(50)
serial int(30) <--- relies on the serials generated by system, stored in the SERIALS TABLE
SERIALS
sn_id int(11)
stock_id int(11) <-- relies on the new stocks inserted in the stocks table
serial int(30) <---- serial NO generated for specific stock.
Where STOCKS inserted needs to store the Serial Number generated for it,
as well as the SERIALS generated must be recorded in the table w/ the stock_id (index/pri) of the stocks being inserted..
This basically means 3 SQL statements / new stock:
get the next auto inc value of serials table (used to generate the serials properly)
insert the stocks into the table with the serials for each
get the insert_id of the said stock and insert that into the serials table
This works but I'm wondering if there's a better approach? So far here's what I got running:
create a serial_lock file on the home directory (this prevents other scripts from issuing new serial numbers to other stocks , = avoiding conflict on concurrent runs..
GENERATE required Serial Nos by getting the next auto_increment value of the serials table and store this in variable for now e.g.
$assigned_serials_array[$index] = $prefix . $index; // results in BN-0001 ("BN-" is the prefix and the rest is padded auto inc value incremented per loop
INSERT INTO stocks , each stock , get the insert_ID
INSERT INTO serials, a record of the serial being issued to that specific stock
after loop is done, delete the lock file
PS.
my original actually does an INSERT already to the serials table, and then does an update on that serials table after a stock_id is generated.. I didn't feel comfortable with that one because of another SQL statement being issued, although it's the safest way though and I don't need to worry about lock file and conflicts.
hmmmm.. any thoughts?
EDIT:
I decided to change my method..
for each SERIAL GENERATED, is a STOCK (stock_id).. I decided to forget about the incremental sequencing of serial numbers 00001 0002 0003
Decided to go ahead and use the stock_id of the specific stock being issued an SN..
so..
get next insert id, generate SN based on that,
INSERT STOCK , w/ generated SN
INSERT SERIAL record, referencing the stock_id to the same next insert id as well..
Done!
I just really wanted to have a perfectly sequenced SN ..
Do not create lock files - this is just wrong.
Instead, DO use transactions. This example in Perl:
my $dbh = DBI->connect("dbi:mysql...", "user", "password");
$dbh->begin_work(); # start new transaction
$dbh->do("INSERT INTO serials ..."); # generate new serial
my $new_serial = $dbh->{mysql_insertid};
$dbh->do("INSERT INTO stocks (..., serialno) VALUES (..., $new_serial)");
# do some more work like inserting into other tables
$dbh->commit(); # finally, commit the transaction
Note that you need to use InnoDB engine for transactions to work
I've been asked if I can keep track of the changes to the records in a MySQL database. So when a field has been changed, the old vs new is available and the date this took place. Is there a feature or common technique to do this?
If so, I was thinking of doing something like this. Create a table called changes. It would contain the same fields as the master table but prefixed with old and new, but only for those fields which were actually changed and a TIMESTAMP for it. It would be indexed with an ID. This way, a SELECT report could be run to show the history of each record. Is this a good method? Thanks!
Here's a straightforward way to do this:
First, create a history table for each data table you want to track (example query below). This table will have an entry for each insert, update, and delete query performed on each row in the data table.
The structure of the history table will be the same as the data table it tracks except for three additional columns: a column to store the operation that occured (let's call it 'action'), the date and time of the operation, and a column to store a sequence number ('revision'), which increments per operation and is grouped by the primary key column of the data table.
To do this sequencing behavior a two column (composite) index is created on the primary key column and revision column. Note that you can only do sequencing in this fashion if the engine used by the history table is MyISAM (See 'MyISAM Notes' on this page)
The history table is fairly easy to create. In the ALTER TABLE query below (and in the trigger queries below that), replace 'primary_key_column' with the actual name of that column in your data table.
CREATE TABLE MyDB.data_history LIKE MyDB.data;
ALTER TABLE MyDB.data_history MODIFY COLUMN primary_key_column int(11) NOT NULL,
DROP PRIMARY KEY, ENGINE = MyISAM, ADD action VARCHAR(8) DEFAULT 'insert' FIRST,
ADD revision INT(6) NOT NULL AUTO_INCREMENT AFTER action,
ADD dt_datetime DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP AFTER revision,
ADD PRIMARY KEY (primary_key_column, revision);
And then you create the triggers:
DROP TRIGGER IF EXISTS MyDB.data__ai;
DROP TRIGGER IF EXISTS MyDB.data__au;
DROP TRIGGER IF EXISTS MyDB.data__bd;
CREATE TRIGGER MyDB.data__ai AFTER INSERT ON MyDB.data FOR EACH ROW
INSERT INTO MyDB.data_history SELECT 'insert', NULL, NOW(), d.*
FROM MyDB.data AS d WHERE d.primary_key_column = NEW.primary_key_column;
CREATE TRIGGER MyDB.data__au AFTER UPDATE ON MyDB.data FOR EACH ROW
INSERT INTO MyDB.data_history SELECT 'update', NULL, NOW(), d.*
FROM MyDB.data AS d WHERE d.primary_key_column = NEW.primary_key_column;
CREATE TRIGGER MyDB.data__bd BEFORE DELETE ON MyDB.data FOR EACH ROW
INSERT INTO MyDB.data_history SELECT 'delete', NULL, NOW(), d.*
FROM MyDB.data AS d WHERE d.primary_key_column = OLD.primary_key_column;
And you're done. Now, all the inserts, updates and deletes in 'MyDb.data' will be recorded in 'MyDb.data_history', giving you a history table like this (minus the contrived 'data_columns' column)
ID revision action data columns..
1 1 'insert' .... initial entry for row where ID = 1
1 2 'update' .... changes made to row where ID = 1
2 1 'insert' .... initial entry, ID = 2
3 1 'insert' .... initial entry, ID = 3
1 3 'update' .... more changes made to row where ID = 1
3 2 'update' .... changes made to row where ID = 3
2 2 'delete' .... deletion of row where ID = 2
To display the changes for a given column or columns from update to update, you'll need to join the history table to itself on the primary key and sequence columns. You could create a view for this purpose, for example:
CREATE VIEW data_history_changes AS
SELECT t2.dt_datetime, t2.action, t1.primary_key_column as 'row id',
IF(t1.a_column = t2.a_column, t1.a_column, CONCAT(t1.a_column, " to ", t2.a_column)) as a_column
FROM MyDB.data_history as t1 INNER join MyDB.data_history as t2 on t1.primary_key_column = t2.primary_key_column
WHERE (t1.revision = 1 AND t2.revision = 1) OR t2.revision = t1.revision+1
ORDER BY t1.primary_key_column ASC, t2.revision ASC
Edit:
Oh wow, people like my history table thing from 6 years ago :P
My implementation of it is still humming along, getting bigger and more unwieldy, I would assume. I wrote views and pretty nice UI to look at the history in this database, but I don't think it was ever used much. So it goes.
To address some comments in no particular order:
I did my own implementation in PHP that was a little more involved, and avoided some of the problems described in comments (having indexes transferred over, signifcantly. If you transfer over unique indexes to the history table, things will break. There are solutions for this in the comments). Following this post to the letter could be an adventure, depending on how established your database is.
If the relationship between the primary key and the revision column seems off it usually means the composite key is borked somehow. On a few rare occasions I had this happen and was at a loss to the cause.
I found this solution to be pretty performant, using triggers as it does. Also, MyISAM is fast at inserts, which is all the triggers do. You can improve this further with smart indexing (or lack of...). Inserting a single row into a MyISAM table with a primary key shouldn't be an operation you need to optimize, really, unless you have significant issues going on elsewhere. In the entire time I was running the MySQL database this history table implementation was on, it was never the cause of any of the (many) performance problems that came up.
if you're getting repeated inserts, check your software layer for INSERT IGNORE type queries. Hrmm, can't remember now, but I think there are issues with this scheme and transactions which ultimately fail after running multiple DML actions. Something to be aware of, at least.
It's important that the fields in the history table and the data table match up. Or, rather, that your data table doesn't have MORE columns than the history table. Otherwise, insert/update/del queries on the data table will fail, when the inserts to the history tables put columns in the query that don't exist (due to d.* in the trigger queries), and the trigger fails. t would be awesome if MySQL had something like schema-triggers, where you could alter the history table if columns were added to the data table. Does MySQL have that now? I do React these days :P
It's subtle.
If the business requirement is "I want to audit the changes to the data - who did what and when?", you can usually use audit tables (as per the trigger example Keethanjan posted). I'm not a huge fan of triggers, but it has the great benefit of being relatively painless to implement - your existing code doesn't need to know about the triggers and audit stuff.
If the business requirement is "show me what the state of the data was on a given date in the past", it means that the aspect of change over time has entered your solution. Whilst you can, just about, reconstruct the state of the database just by looking at audit tables, it's hard and error prone, and for any complicated database logic, it becomes unwieldy. For instance, if the business wants to know "find the addresses of the letters we should have sent to customers who had outstanding, unpaid invoices on the first day of the month", you likely have to trawl half a dozen audit tables.
Instead, you can bake the concept of change over time into your schema design (this is the second option Keethanjan suggests). This is a change to your application, definitely at the business logic and persistence level, so it's not trivial.
For example, if you have a table like this:
CUSTOMER
---------
CUSTOMER_ID PK
CUSTOMER_NAME
CUSTOMER_ADDRESS
and you wanted to keep track over time, you would amend it as follows:
CUSTOMER
------------
CUSTOMER_ID PK
CUSTOMER_VALID_FROM PK
CUSTOMER_VALID_UNTIL PK
CUSTOMER_STATUS
CUSTOMER_USER
CUSTOMER_NAME
CUSTOMER_ADDRESS
Every time you want to change a customer record, instead of updating the record, you set the VALID_UNTIL on the current record to NOW(), and insert a new record with a VALID_FROM (now) and a null VALID_UNTIL. You set the "CUSTOMER_USER" status to the login ID of the current user (if you need to keep that). If the customer needs to be deleted, you use the CUSTOMER_STATUS flag to indicate this - you may never delete records from this table.
That way, you can always find what the status of the customer table was for a given date - what was the address? Have they changed name? By joining to other tables with similar valid_from and valid_until dates, you can reconstruct the entire picture historically. To find the current status, you search for records with a null VALID_UNTIL date.
It's unwieldy (strictly speaking, you don't need the valid_from, but it makes the queries a little easier). It complicates your design and your database access. But it makes reconstructing the world a lot easier.
You could create triggers to solve this. Here is a tutorial to do so (archived link).
Setting constraints and rules in the database is better than writing
special code to handle the same task since it will prevent another
developer from writing a different query that bypasses all of the
special code and could leave your database with poor data integrity.
For a long time I was copying info to another table using a script
since MySQL didn’t support triggers at the time. I have now found this
trigger to be more effective at keeping track of everything.
This trigger will copy an old value to a history table if it is changed
when someone edits a row. Editor ID and last mod are stored in the
original table every time someone edits that row; the time corresponds
to when it was changed to its current form.
DROP TRIGGER IF EXISTS history_trigger $$
CREATE TRIGGER history_trigger
BEFORE UPDATE ON clients
FOR EACH ROW
BEGIN
IF OLD.first_name != NEW.first_name
THEN
INSERT INTO history_clients
(
client_id ,
col ,
value ,
user_id ,
edit_time
)
VALUES
(
NEW.client_id,
'first_name',
NEW.first_name,
NEW.editor_id,
NEW.last_mod
);
END IF;
IF OLD.last_name != NEW.last_name
THEN
INSERT INTO history_clients
(
client_id ,
col ,
value ,
user_id ,
edit_time
)
VALUES
(
NEW.client_id,
'last_name',
NEW.last_name,
NEW.editor_id,
NEW.last_mod
);
END IF;
END;
$$
Another solution would be to keep an Revision field and update this field on save. You could decide that the max is the newest revision, or that 0 is the most recent row. That's up to you.
Here is how we solved it
a Users table looked like this
Users
-------------------------------------------------
id | name | address | phone | email | created_on | updated_on
And the business requirement changed and we were in a need to check all previous addresses and phone numbers a user ever had.
new schema looks like this
Users (the data that won't change over time)
-------------
id | name
UserData (the data that can change over time and needs to be tracked)
-------------------------------------------------
id | id_user | revision | city | address | phone | email | created_on
1 | 1 | 0 | NY | lake st | 9809 | #long | 2015-10-24 10:24:20
2 | 1 | 2 | Tokyo| lake st | 9809 | #long | 2015-10-24 10:24:20
3 | 1 | 3 | Sdny | lake st | 9809 | #long | 2015-10-24 10:24:20
4 | 2 | 0 | Ankr | lake st | 9809 | #long | 2015-10-24 10:24:20
5 | 2 | 1 | Lond | lake st | 9809 | #long | 2015-10-24 10:24:20
To find the current address of any user, we search for UserData with revision DESC and LIMIT 1
To get the address of a user between a certain period of time
we can use created_on bewteen (date1 , date 2)
MariaDB supports System Versioning since 10.3 which is the standard SQL feature that does exactly what you want: it stores history of table records and provides access to it via SELECT queries. MariaDB is an open-development fork of MySQL. You can find more on its System Versioning via this link:
https://mariadb.com/kb/en/library/system-versioned-tables/
Why not simply use bin log files? If the replication is set on the Mysql server, and binlog file format is set to ROW, then all the changes could be captured.
A good python library called noplay can be used. More info here.
Just my 2 cents. I would create a solution which records exactly what changed, very similar to transient's solution.
My ChangesTable would simple be:
DateTime | WhoChanged | TableName | Action | ID |FieldName | OldValue
1) When an entire row is changed in the main table, lots of entries will go into this table, BUT that is very unlikely, so not a big problem (people are usually only changing one thing)
2) OldVaue (and NewValue if you want) have to be some sort of epic "anytype" since it could be any data, there might be a way to do this with RAW types or just using JSON strings to convert in and out.
Minimum data usage, stores everything you need and can be used for all tables at once. I'm researching this myself right now, but this might end up being the way I go.
For Create and Delete, just the row ID, no fields needed. On delete a flag on the main table (active?) would be good.
The direct way of doing this is to create triggers on tables. Set some conditions or mapping methods. When update or delete occurs, it will insert into 'change' table automatically.
But the biggest part is what if we got lots columns and lots of table. We have to type every column's name of every table. Obviously, It's waste of time.
To handle this more gorgeously, we can create some procedures or functions to retrieve name of columns.
We can also use 3rd-part tool simply to do this. Here, I write a java program
Mysql Tracker
In MariaDB 10.5+ this is as easy to setup as
CREATE TABLE t (x INT) WITH SYSTEM VERSIONING
PARTITION BY SYSTEM_TIME;
Past history can then be queried by doing
SELECT * FROM t FOR SYSTEM_TIME AS OF TIMESTAMP '2016-10-09 08:07:06';
There is currently no counterpart for this in MySQL.
See the documentation for more info. If you're on an older version of MariaDB, the documentation has an alternate syntax that has been available since MariaDB 10.3.4.
Assume that i have two strings like the following.
$sa = "12,20,45"; $sb = "13,20,50";
I want to check whether any of the number in sa present in sb with back reference so that i can get those numbers back and do some calculation.
The numbers are nothing but unique id's in database. So i am checking whether the ids in sa is present in the list of ids in sb.
Besides if it is possible to get all matching and non matching ids then that would be nice.
For this it doesn't have to be one operation. Multiple operations is fine.(like executing match twice or more).
What i am trying to do is i am creating subscribers and they are assigned to groups.
I create newsletters and will assign to groups.
If i try to assign a newsletter to the same group then i want the group id so that i can exempt that group and assign that newsletter to the rest.
so if group 15,16,17 are already assigned with a newsletter and the next time i am trying to assign group 15,20,21 i want 15 to be exempted and i want the newsletter to be assigned to 20,21.
And... If i could get a mysql example too then that could be nice.
Any type of answer if it could help the please post it.
THX
first of all, this is not a problem you would want to solve with regex. At.all.
Second, you shouldn't have a list of Ids as values in your database, especially if you need to look up on them. It's inefficient and bad database design.
If you only require to link subscribers to newletters these would be the tables you need, one table per entity and a junction table for joining. I have left out the foreign key constraints.
CREATE TABLE Subscribers
(subscriber_id bigint,
first_name varchar(50),
... )
CREATE TABLE Newsletter
(news_letter_id bigint,
name varchar(50),
... )
CREATE TABLE NewslettersSubscribers [or just "Subscriptions"]
(news_letter_id bigint,
subscriber_id bigint,
payment_type smallint,
...[other variables that are specific to this subscription]
)
If you would rather have your subscribers in a group and each subscriber can be in many groups, it would look like this.
CREATE TABLE Subscribers
(subscriber_id bigint,
first_name varchar(50)
... )
CREATE TABLE Group
(group_id bigint,
group_name varchar(50),
... )
CREATE TABLE SubscribersGroups --[or just "Membership"]
(subscriber_id bigint,
group_id bigint,
payment_type smallint,
--...[other variables that are specific to this membership]
)
CREATE TABLE Newsletter
(news_letter_id bigint,
name varchar(50),
... )
CREATE TABLE NewslettersGroups --[or just "SubscriptionGroups"]
(news_letter_id bigint,
group_id bigint
--...[possibly variables that are specific to this subscription group]
)
Now your actions are rather simple. In your example we have newsletter 1, and we have groups 15, 16, 17, 20 and 21 and possibly other groups. We also have these values in NewslettersGroups
| news_letter_id | group_id |
| 1 | 15 |
| 1 | 16 |
| 1 | 17 |
Now you want to connect newsletter 1 to 20 and 21 (only you think you need to do 15 as well). So just insert where it's needed (I'm not 100% sure if this syntax works, I don't use MySQL, but see this reference)
INSERT INTO NewslettersGroups VALUES (1,15),(1,20), (1,21)
ON DUPLICATE KEY UPDATE;
I have a table with an auto_increment field and sometimes rows get deleted so auto_increment leaves gaps. Is there any way to avoid this or if not, at the very least, how to write an SQL query that:
Alters the auto_increment value to be the max(current value) + 1
Return the new auto_increment value?
I know how to write part 1 and 2 but can I put them in the same query?
If that is not possible:
How do I "select" (return) the auto_increment value or auto_increment value + 1?
Renumbering will cause confusion. Existing reports will refer to record 99, and yet if the system renumbers it may renumber that record to 98, now all reports (and populated UIs) are wrong. Once you allocate a unique ID it's got to stay fixed.
Using ID fields for anything other than simple unique numbering is going to be problematic. Having a requirement for "no gaps" is simply inconsistent with the requirement to be able to delete. Perhaps you could mark records as deleted rather than delete them. Then there are truly no gaps. Say you are producing numbered invoices: you would have a zero value cancelled invoice with that number rather than delete it.
There is a way to manually insert the id even in an autoinc table. All you would have to do is identify the missing id.
However, don't do this. It can be very dangerous if your database is relational. It is possible that the deleted id was used elsewhere. When removed, it would not present much of an issue, perhaps it would orphan a record. If replaced, it would present a huge issue because the wrong relation would be present.
Consider that I have a table of cars and a table of people
car
carid
ownerid
name
person
personid
name
And that there is some simple data
car
1 1 Van
2 1 Truck
3 2 Car
4 3 Ferrari
5 4 Pinto
person
1 Mike
2 Joe
3 John
4 Steve
and now I delete person John.
person
1 Mike
2 Joe
4 Steve
If I added a new person, Jim, into the table, and he got an id which filled the gap, then he would end up getting id 3
1 Mike
2 Joe
3 Jim
4 Steve
and by relation, would be the owner of the Ferrari.
I generally agree with the wise people on this page (and duplicate questions) advising against reusing auto-incremented id's. It is good advice, but I don't think it's up to us to decide the rights or wrongs of asking the question, let's assume the developer knows what they want to do and why.
The answer is, as mentioned by Travis J, you can reuse an auto-increment id by including the id column in an insert statement and assigning the specific value you want.
Here is a point to put a spanner in the works: MySQL itself (at least 5.6 InnoDB) will reuse an auto-increment ID in the following circumstance:
delete any number rows with the highest auto-increment id
Stop and start MySQL
insert a new row
The inserted row will have an id calculated as max(id)+1, it does not continue from the id that was deleted.
As djna said in her/his answer, it's not a good practice to alter database tables in such a way, also there is no need to that if you have been choosing the right scheme and data types. By the way according to part od your question:
I have a table with an auto_increment field and sometimes rows get deleted so auto_increment leaves gaps. Is there any way to avoid this?
If your table has too many gaps in its auto-increment column, probably as a result of so many test INSERT queries
And if you want to prevent overwhelming id values by removing the gaps
And also if the id column is just a counter and has no relation to any other column in your database
, this may be the thing you ( or any other person looking for such a thing ) are looking for:
SOLUTION
remove the original id column
add it again using auto_increment on
But if you just want to reset the auto_increment to the first available value:
ALTER TABLE `table_name` AUTO_INCREMENT=1
not sure if this will help, but in sql server you can reseed the identity fields. It seems there's an ALTER TABLE statement in mySql to acheive this. Eg to set the id to continue at 59446.
ALTER TABLE table_name AUTO_INCREMENT = 59446;
I'm thinking you should be able to combine a query to get the largest value of auto_increment field, and then use the alter table to update as needed.