#1071 - Specified key was too long; max key length is 767 bytes - mysql

I this SQL query to create a table:
CREATE TABLE IF NOT EXISTS `local_sysDB`.`hashtags` (
`id` INT NOT NULL AUTO_INCREMENT,
`hashtag` VARCHAR(255) NOT NULL COMMENT 'hashtag must be unique. Must be saved without #',
`accountId` INT NULL,
`startTracking` DATETIME NOT NULL COMMENT 'When tracking of the hashtag start',
`endTracking` DATETIME NOT NULL COMMENT 'When tracking of the hashtag ends',
`channelInstagram` TINYINT(1) NOT NULL DEFAULT 1,
`channelTwitter` TINYINT(1) NOT NULL DEFAULT 1,
`channelYoutube` TINYINT(1) NOT NULL DEFAULT 1,
`postLimit` INT NOT NULL,
`suspendOnLimit` TINYINT(1) NOT NULL DEFAULT 1,
`created` TIMESTAMP NOT NULL DEFAULT NOW(),
`updated` TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP,
`approveBeforeView` TINYINT(1) NOT NULL DEFAULT 0 COMMENT 'If account should approve posts before being displayed public',
`suspended` TINYINT(1) NOT NULL DEFAULT 0,
`InstagramSubscriptionId` INT(10) UNSIGNED NULL,
`deleted` TINYINT(1) NULL DEFAULT 0 COMMENT 'if hashtag is marked for deletion',
`collectedPosts` BIGINT(50) UNSIGNED NULL DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE INDEX `hashtags_hashtag` (`hashtag` ASC) KEY_BLOCK_SIZE=255,
INDEX `hashtags_accounts_accountId` (`accountId` ASC),
INDEX `hashtag_trackingDate` (`startTracking` ASC, `endTracking` ASC),
INDEX `hashtag_collectedPosts` (`collectedPosts` ASC),
INDEX `hashtag_updated` (`updated` ASC),
FULLTEXT INDEX `hashtag_search` (`hashtag` ASC),
CONSTRAINT `hashtags_accounts_accountId`
FOREIGN KEY (`accountId`)
REFERENCES `local_sysDB`.`accounts` (`id`)
ON DELETE CASCADE
ON UPDATE CASCADE)
ENGINE = InnoDB
ROW_FORMAT = COMPRESSED
KEY_BLOCK_SIZE = 16;
When I try to run this, I get the following error:
SQL-query:
CREATE TABLE IF NOT EXISTS `local_sysDB`.`hashtags` (
`id` INT NOT NULL AUTO_INCREMENT,
`hashtag` VARCHAR(255) NOT NULL COMMENT 'hashtag must be unique. Must be saved without #',
`accountId` INT NULL,
`startTracking` DATETIME NOT NULL COMMENT 'When tracking of the hashtag start',
`endTracking` DATETIME NOT NULL COMMENT 'When tracking of the hashtag ends',
`channelInstagram` TINYINT(1) NOT NULL DEFAULT 1,
`channelTwitter` TINYINT(1) NOT NULL DEFAULT 1,
`channelYoutube` TINYINT(1) NOT NULL DEFAULT 1,
`postLimit` INT NOT NULL,
`suspendOnLimit` TINYINT(1) NOT NULL DEFAULT 1,
`created` TIMESTAMP NOT NULL DEFAULT NOW(),
`updated` TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP,
`approveBeforeView` TINYINT(1) NOT NULL DEFAULT 0 COMMENT 'If account should approve posts before being displayed public',
`suspended` TINYINT(1) NOT NULL DEFAULT 0,
`InstagramSubscriptionId` INT(10) UNSIGNED NULL,
`deleted` TINYINT(1) NULL DEFAULT 0 COMMENT 'if hashtag is [...]
MySQL meldt: Documentatie
#1071 - Specified key was too long; max key length is 767 bytes
I already found out it has something to do with this:
767 bytes is the stated prefix limitation for InnoDB tables - its
1,000 bytes long for MyISAM tables.
According to the response to this issue, you can get the key to apply
by specifying a subset of the column rather than the entire amount.
IE:
ALTER TABLE mytable ADD UNIQUE ( column1(15), column2(200) ); Tweak
as you need to get the key to apply, but I wonder if it would be worth
it to review your data model regarding this entity to see if there's
improvements that would allow you to implement the intended business
rules without hitting the MySQL limitation.
I tried adding a length to my indexes, but MySQL Workbench keeps resetting them to 0.
I'd like to know if there could be another cause of this problem, or another way to solve this problem.

I just learned a workaround... Get 5.5.14 or 5.6.3 (or later), do the SETs indicated here, and use DYNAMIC or COMPRESSED:
SET GLOBAL innodb_file_per_table = ON,
innodb_file_format = Barracuda,
innodb_large_prefix = ON;
CREATE TABLE so29676724 (
`id` INT NOT NULL AUTO_INCREMENT,
`hashtag` VARCHAR(255) NOT NULL COMMENT 'hashtag must be unique. Must be saved without #',
PRIMARY KEY (`id`),
UNIQUE INDEX `hashtags_hashtag` (`hashtag` ASC)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET utf8mb4
ROW_FORMAT = COMPRESSED;
SHOW CREATE TABLE so29676724\G
mysql> CREATE TABLE so29676724 (
-> `id` INT NOT NULL AUTO_INCREMENT,
-> `hashtag` VARCHAR(255) NOT NULL COMMENT 'hashtag must be unique. Must be saved without #',
-> PRIMARY KEY (`id`),
-> UNIQUE INDEX `hashtags_hashtag` (`hashtag` ASC)
-> )
-> ENGINE = InnoDB
-> DEFAULT CHARACTER SET utf8mb4
-> ROW_FORMAT = COMPRESSED;
Query OK, 0 rows affected (0.09 sec)

"Hashes" are usually hex, not UTF-8. Hashes are usually much shorter than 255.
If either applies, then...
`hashtag`
VARCHAR(160) -- this
CHARACTER SET ascii -- and/or this
would be a solution that would work on any version without any of the innodb settings indicated.
(Note: 191 is the cutoff for VARCHAR with utf8mb4, but few standard hashes need that much.)

Related

MySQL Error #1215 - Cannot Add Foreign Key Constraint

I have the following problem with a pretty simple MySQL database table creation:
SQL query:
CREATE TABLE IF NOT EXISTS comments(
commentId INT NOT NULL AUTO_INCREMENT,
comment VARCHAR(1024) NOT NULL,
commentdate TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
profileId INT NOT NULL,
PRIMARY KEY(commentId),
CONSTRAINT fk_ProfilesProfileId FOREIGN KEY(profileId) REFERENCES profiles.profileId ON UPDATE RESTRICT ON DELETE CASCADE
)Engine=InnoDB DEFAULT CHARSET utf8 COLLATE utf8_general_ci
Error:
MySQL said: Documentation
#1215 - Cannot add foreign key constraint
As you can see, the error number is 1215, which would indicate that the problem is about Foreign Key and Primary Key in comments and profiles tables (respectively) being different types. However, I have checked multiple times that they both are INT(11) and signed.
Here are queries for both of them:
'profiles' -table
CREATE TABLE IF NOT EXISTS profiles(
profileId INT NOT NULL AUTO_INCREMENT,
corpname VARCHAR(512) NOT NULL DEFAULT 'VIRHE',
corpserial VARCHAR(16) NOT NULL DEFAULT 'VIRHE',
loginusername VARCHAR(128) NOT NULL,
loginpassword VARCHAR(128) NOT NULL,
profilephone VARCHAR(16),
mapsphone VARCHAR(16),
added TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
done TIMESTAMP DEFAULT NULL,
coderequest TIMESTAMP DEFAULT NULL,
confirmed TIMESTAMP DEFAULT NULL,
PRIMARY KEY(profileId),
INDEX corpnameIndex (corpname),
INDEX corpserialIndex (corpserial)
)Engine=InnoDB DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
'comments' -table
CREATE TABLE IF NOT EXISTS comments(
commentId INT NOT NULL AUTO_INCREMENT,
comment VARCHAR(1024) NOT NULL,
commentdate TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
profileId INT NOT NULL,
PRIMARY KEY(commentId),
CONSTRAINT fk_ProfilesProfileId FOREIGN KEY(profileId) REFERENCES profiles.profileId ON UPDATE RESTRICT ON DELETE CASCADE
)Engine=InnoDB DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
It is possible that I am dumb, and I'm just missing something. Hopefully it's so.
I replicated your example locally. It seems that the problem is how you declare your constraint for the foreign key.
CREATE TABLE profiles (
profileId int(11) NOT NULL AUTO_INCREMENT,
corpname varchar(512) NOT NULL DEFAULT 'VIRHE',
corpserial varchar(16) NOT NULL DEFAULT 'VIRHE',
loginusername varchar(128) NOT NULL,
loginpassword varchar(128) NOT NULL,
profilephone varchar(16) DEFAULT NULL,
mapsphone varchar(16) DEFAULT NULL,
added timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
done timestamp NULL DEFAULT NULL,
coderequest timestamp NULL DEFAULT NULL,
confirmed timestamp NULL DEFAULT NULL,
PRIMARY KEY (profileId),
KEY corpnameIndex (corpname(255)),
KEY corpserialIndex (corpserial)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS comments(
commentId INT NOT NULL AUTO_INCREMENT,
comment VARCHAR(1024) NOT NULL,
commentdate TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
profileId INT NOT NULL,
PRIMARY KEY(commentId),
CONSTRAINT fk_ProfilesProfileId FOREIGN KEY(profileId) REFERENCES profiles(profileId) ON UPDATE RESTRICT ON DELETE CASCADE
)Engine=InnoDB DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
with respect to your code:
The syntax for the foreign key after REFERENCES is table(field) rather than table.field
TIMESTAMP DEFAULT NULL should be TIMESTAMP NULL.
Note the partial index corpnameIndex, some considerations follow hereunder.
Some information about indexes the engine you want to use is InnoDB, which specifies that (from here):
By default, an index key for a single-column index can be up to 767
bytes. The same length limit applies to any index key prefix. See
Section 13.1.13, “CREATE INDEX Syntax”. For example, you might hit
this limit with a column prefix index of more than 255 characters on a
TEXT or VARCHAR column, assuming a UTF-8 character set and the maximum
of 3 bytes for each character. When the innodb_large_prefix
configuration option is enabled, this length limit is raised to 3072
bytes, for InnoDB tables that use the DYNAMIC and COMPRESSED row
formats.
The problem in your table is INDEX corpnameIndex (corpname), because this index would be 512*3 = 1536 bytes long.
You have some options here:
Change corpname size to 255 by declaring it as follows corpname VARCHAR(255) NOT NULL DEFAULT 'VIRHE'
Enable innodb_large_prefix on your mysql by running set global innodb_large_prefix = ON;. This will bring the limit to 3072 bytes (1024 chars).
Index only a part of your corpname field by using ADD INDEX (corpname(255))

MySQL MyISAM - not able to add a new column to table

I have the following table:
mysql> show create table keyword_links\G
*************************** 1. row ***************************
Table: keyword_links
Create Table: CREATE TABLE `keyword_links` (
`keyword_id` int(11) NOT NULL AUTO_INCREMENT,
`keyword` tinytext NOT NULL,
`link` tinytext,
`weight` smallint(6) NOT NULL DEFAULT '0',
`class_id` smallint(6) NOT NULL DEFAULT '0',
`category_id` smallint(6) NOT NULL DEFAULT '0',
`timestamp` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`keyword_id`),
KEY `class_id` (`class_id`),
KEY `category_id` (`category_id`),
KEY `idx_keyword` (`keyword`(333))
) ENGINE=MyISAM AUTO_INCREMENT=5082 DEFAULT CHARSET=utf8
to which I am trying to add a new column, which is failing:
mysql> ALTER TABLE keyword_links ADD COLUMN list_id INT UNSIGNED NOT NULL DEFAULT 0;
ERROR 1170 (42000): BLOB/TEXT column 'keyword' used in key specification without a key length
The index on keyword column does have a key length of 333, so why is it failing and how to fix it?
UPDATE
I tried reducing the size of the index on the keyword column from 333 to 255 and now I am able to add the new column successfully:
ALTER TABLE keyword_links DROP INDEX idx_keyword;
CREATE INDEX index_keyword ON keyword_links (keyword(255));
ALTER TABLE keyword_links ADD COLUMN list_id INT UNSIGNED NOT NULL DEFAULT 0;
But I would still like to know what's going on.
This is because of myisam's index's length limit of 1000 bytes (see this link)
Please note that this is all about bytes, and that a character might take 2, 3 or even 4 bytes depending on your encoding, and also that an INT will always be 4 bytes.
Index key will allow only 255 i assume.

MySQL - convert MyISAM into InnoDB getting error 1075

I am trying to convert a table from MyISAM into InnoDB, this is the definition and I am getting error #1075 - Incorrect table definition; there can be only one auto column and it must be defined as a key
The table has an AutoIncrement value and the field is indexed and it works with MyISAM. I am new to InnoDB so it might be a dumb question
CREATE TABLE `cart_item` (
`cart_id` int(10) unsigned NOT NULL DEFAULT '0',
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`design_number` int(10) unsigned NOT NULL,
`logo_position_id` smallint(5) unsigned NOT NULL,
`subst_style_id` varchar(10) DEFAULT NULL,
`style_id` varchar(10) NOT NULL DEFAULT '',
`subst_color_id` smallint(5) unsigned DEFAULT NULL,
`color_id` smallint(5) unsigned NOT NULL,
`size_id` smallint(5) unsigned NOT NULL,
`qty` mediumint(8) unsigned NOT NULL,
`active` enum('y','n') NOT NULL DEFAULT 'y',
`date_last_modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`last_modified_by_id` mediumint(5) unsigned NOT NULL,
`date_last_locked` datetime DEFAULT NULL,
`last_locked_by_id` smallint(5) unsigned NOT NULL,
`date_added` datetime NOT NULL,
`subsite_logo_group_id` int(11) NOT NULL,
`bundle` varchar(32) NOT NULL,
`color_stop_1` varchar(4) DEFAULT NULL,
PRIMARY KEY (`cart_id`,`id`),
KEY `color_id` (`color_id`),
KEY `style_id` (`style_id`),
KEY `size_id` (`size_id`),
KEY `design_number` (`design_number`),
KEY `subsite_logo_group_id` (`subsite_logo_group_id`),
KEY `date_added` (`date_added`),
KEY `bundle` (`bundle`)
) ENGINE=InnoDB
What you were doing on the MyISAM table, cannot be done with InnoDB. See my answer on a (similar) problem: creating primary key based on date
MySQL docs, in the Using AUTO_INCREMENT section, explain it:
For MyISAM tables you can specify AUTO_INCREMENT on a secondary column in a multiple-column index. In this case, the generated value for the AUTO_INCREMENT column is calculated as MAX(auto_increment_column) + 1 WHERE prefix=given-prefix. This is useful when you want to put data into ordered groups.
You may get similar behaviour in InnoDB but not with AUTO_INCREMENT. You'll have to use either some fancy trigger or a stored procedure for your Inserts that will take care of the (per cart_id) auto-increment.
You have a composite PRIMARY KEY defined on (cart_id, id), but the AUTO_INCREMENT requires an index on id alone. You can add a KEY for it (not a primary key, but just a plain index):
KEY `idx_id` (`id`)
I question the use of the composite PK on (cart_id, id) though, since id is alone a unique value by definition. Perhaps you should make id the PK, and create a separate index across the combination.
PRIMARY KEY (`id`),
KEY (`cart_id`, `id`)
It doesn't even need to be specified as UNIQUE because the AUTO_INCREMENT can't be repeated anyway. There is no way to violate uniqueness on the combination (cart_id, id).
AUTO_INCREMENT columns should be define as key, as what the error implies.
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
and set UNIQUE on the two column instead of primary key
UNIQUE (`cart_id`,`id`),
SQLFiddle Demo

proper index (or removal) to optimize a large data set table

We have a 'visitor' tracking schema going on - that when pushed, seems to be causing some strain on the DB server.
VISITORS table identifies unique users by a HASH (current records 310,000). A search is performed on the hash, and if not found, it is added. The ID is needed for the following two tables
CREATE TABLE visitors (
id int(10) UNSIGNED NOT NULL auto_increment,
ip varchar(25) NOT NULL,
hash varchar(64) NOT NULL,
first_visit varchar(32) NOT NULL,
created_at datetime NOT NULL default '0000-00-00 00:00:00',
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE visitors ADD UNIQUE INDEX (hash);
ALTER TABLE visitors ADD INDEX (created_at);
VISITOR_VISITS table identifies when a user visited only when we can identify some referral sources (current count 142,000). A search is performed looking for the visitor_id, type and visit_date. If there is nothing found - it is added. The ID is used in the following table.
CREATE TABLE visitor_visits (
id int(10) UNSIGNED NOT NULL auto_increment,
visitor_id int(10) UNSIGNED NOT NULL,
source varchar(64) NULL DEFAULT NULL DEFAULT NULL,
medium varchar(64) NULL DEFAULT NULL,
campaign varchar(256) NULL DEFAULT NULL,
page varchar(32) NULL DEFAULT NULL,
landing varchar(32) NULL DEFAULT NULL,
type enum('fundraiser_view') NULL DEFAULT NULL,
visit_date date NOT NULL default '0000-00-00',
created_at datetime NOT NULL default '0000-00-00 00:00:00',
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE visitor_visits ADD UNIQUE INDEX (visitor_id,type,visit_date);
ALTER TABLE visitor_visits ADD CONSTRAINT FK_visits_visitor_id FOREIGN KEY (visitor_id) REFERENCES visitors(id);
PAGE_VIEWS logs individual page views (not all pages, just pages we are tracking). It can be linked to a visitor and can reference a visitor_visit (current count 2.4million -- reason it is higher is we started micro-visitor logging after logging individual pages). An insert/on duplicate query is used to add the record to this based on the view_date for the identified user. Since the ID is not needed, a pure lookup query isnt required
CREATE TABLE page_views (
id int(10) UNSIGNED NOT NULL auto_increment,
page_id int(10) UNSIGNED NOT NULL,
current_donations decimal(10,2) NOT NULL DEFAULT 0,
ip varchar(25) NOT NULL,
hash varchar(32) NOT NULL,
visitor_id int(10) UNSIGNED NULL DEFAULT NULL AFTER,
visitor_visit_id int(10) UNSIGNED NULL DEFAULT NULL AFTER,
page_views int(10) UNSIGNED NOT NULL DEFAULT 0,
widget_views int(10) UNSIGNED NOT NULL DEFAULT 0,
view_date date NOT NULL,
viewed_at datetime NOT NULL default '0000-00-00 00:00:00',
created_at datetime NOT NULL default '0000-00-00 00:00:00',
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE page_views ADD UNIQUE INDEX (page_id,view_date,visitor_id,hash);
ALTER TABLE page_views ADD INDEX (visitor_id);
ALTER TABLE page_views ADD INDEX (visitor_visit_id);
ALTER TABLE page_views ADD CONSTRAINT FK_page_views_page_id FOREIGN KEY (page_id) REFERENCES pages(id);
ALTER TABLE page_views ADD CONSTRAINT FK_page_views_visitor_id FOREIGN KEY (visitor_id) REFERENCES visitors(id);
ALTER TABLE page_views ADD CONSTRAINT FK_page_views_visit_id FOREIGN KEY (visitor_visit_id) REFERENCES visitor_visits(id);
Last week, our site got a inflow of people due to a news article, and this visitor identifying rall bottlenecked performance. I am wondering if there is an obvious optimization in there. Could it be the foreign key constraints ? Over indexing? Need for better indexing?
Try this ::
1) Index on varchar doesn't much improve the performance.
2) Try to partition the table, on a date range.
You didn't tell us what is bottlenecking your database, so I just guess it's InnoDB concurrent writes. If it isn't so and the problem is only with SELECTs (which I doubt), you should show us the exact queries. You could try to reduce the write performance hit by creating a staging table and then bulk-moving stuff from in to the main table:
CREATE TABLE page_views_tmp (
id int(10) UNSIGNED NOT NULL auto_increment,
page_id int(10) UNSIGNED NOT NULL,
current_donations decimal(10,2) NOT NULL DEFAULT 0,
ip varchar(25) NOT NULL,
hash varchar(32) NOT NULL,
visitor_id int(10) UNSIGNED NULL DEFAULT NULL AFTER,
visitor_visit_id int(10) UNSIGNED NULL DEFAULT NULL AFTER,
page_views int(10) UNSIGNED NOT NULL DEFAULT 0,
widget_views int(10) UNSIGNED NOT NULL DEFAULT 0,
view_date date NOT NULL,
viewed_at datetime NOT NULL default '0000-00-00 00:00:00',
created_at datetime NOT NULL default '0000-00-00 00:00:00',
PRIMARY KEY (id)
) ENGINE=MEMORY DEFAULT CHARSET=utf8;
And then, once per a couple of seconds or after this table has a considerable amount of rows in it:
START TRANSACTION;
INSERT INTO page_views SELECT * FROM page_views_tmp;
DELETE FROM page_views_tmp;
COMMIT;

MySQL Slow query: count articles, group by category, any way to optimize?

Is there any way to optimize this query? It takes more than 2.5 secs.
SELECT articles.categories_id,
COUNT(articles.id) AS count
FROM articles
WHERE articles.created >= DATE_SUB(NOW(), INTERVAL 48 HOUR)
GROUP BY articles.categories_id
ORDER BY count DESC
CREATE TABLE IF NOT EXISTS `articles` (
`id` int(11) NOT NULL,
`categories_id` int(11) NOT NULL,
`feeds_id` int(11) NOT NULL DEFAULT '0',
`users_id` int(11) NOT NULL DEFAULT '1',
`title` varchar(255) CHARACTER SET utf8 NOT NULL,
`sefriendly` varchar(255) CHARACTER SET utf8 NOT NULL,
`body` text CHARACTER SET utf8,
`source` varchar(255) CHARACTER SET utf8 DEFAULT NULL,
`created` datetime NOT NULL,
`edited` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`fingerprint` varchar(32) CHARACTER SET utf8 NOT NULL,
`type` int(1) NOT NULL DEFAULT '1' COMMENT '1 => Feed fetched article\n2 => User submitted article',
`description` varchar(255) CHARACTER SET utf8 DEFAULT NULL,
`keywords` text CHARACTER SET utf8,
`status` int(1) NOT NULL DEFAULT '0' COMMENT '0 => Passive\n1 => Active\n2 => Pending',
PRIMARY KEY (`id`),
KEY `categories_id` (`categories_id`) USING BTREE,
KEY `feeds_id` (`feeds_id`) USING BTREE,
KEY `users_id` (`users_id`) USING BTREE,
KEY `fingerprint` (`fingerprint`) USING BTREE,
KEY `title` (`title`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_turkish_ci ROW_FORMAT=COMPACT;
I already use caching, so there is no problem in terms of code.
This is the explain sql result:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE articles index NULL categories_id 4 NULL 120411 Using where; Using temporary; Using file sort
Thanks.
You can improve things by adding an index on created. This will help serve your WHERE clause:
WHERE articles.created >= DATE_SUB(NOW(), INTERVAL 48 HOUR)
It will probably be advantageous to instead create a covering index on (created, categories_id) so that all the data required for the query is available in the index.
Note that the value of id is not needed for this query because COUNT only cares if the value is NULL or NOT NULL, but id is defined to be NOT NULL in your table definition. It would probably be a good idea to make this explicit by using COUNT(*) or COUNT(1) instead of COUNT(id) as this is guaranteed to give the same result. But I would expect that MySQL is intelligent enough to make this optimization for you automatically.
I don't think that you can avoid the file sort because you are sorting on the result of an aggregation, and this cannot be indexed.