Some SQL servers allow for a generic statement such as ORDER BY PRIMARY KEY. I don't believe this works for MySQL, is there any such workaround that would allow for automated selects across multiple tables or does it require a lookup query to determine the primary key?
The workaround I have been working on involves calling a SHOW COLUMNS FROM before running the query. Is there a more efficient way of doing this? Can MySQL determine the primary key of a table during the select process?
Update: There is no official way of doing this in MySQL or SQL in general as Gordon pointed out. SAP has custom functionality for it. There are workarounds, such as working with SHOW COLUMNS FROM table or the information_schema as John pointed out.
MySQL generally pulls data out by insertion order which would be by primary key, but that aside you technically can do the same thing if you pull out the primary key column name and put it in an order by
SELECT whatever FROM table
ORDER BY
( SELECT `COLUMN_NAME`
FROM `information_schema`.`COLUMNS`
WHERE (`TABLE_SCHEMA` = 'dbName')
AND (`TABLE_NAME` = 'tableName')
AND (`COLUMN_KEY` = 'PRI')
);
For composite keys you can use this
SELECT whatever FROM table
ORDER BY
( SELECT GROUP_CONCAT(`COLUMN_NAME` SEPARATOR ', ')
FROM `information_schema`.`COLUMNS`
WHERE (`TABLE_SCHEMA` = 'dbName')
AND (`TABLE_NAME` = 'tableName')
AND (`COLUMN_KEY` = 'PRI')
);
Permission for information schema access from the DOCS
Each MySQL user has the right to access these tables, but
can see only the rows in the tables that correspond to objects for
which the user has the proper access privileges. In some cases (for
example, the ROUTINE_DEFINITION column in the
INFORMATION_SCHEMA.ROUTINES table), users who have insufficient
privileges see NULL. These restrictions do not apply for InnoDB
tables; you can see them with only the PROCESS privilege.
The same privileges apply to selecting information from
INFORMATION_SCHEMA and viewing the same information through SHOW
statements. In either case, you must have some privilege on an object
to see information about it.
SETUP:
CREATE TABLE some_stuff (
firstID INT,
secondID INT,
username varchar(55),
PRIMARY KEY (firstID, secondID)
) ;
QUERY:
SELECT GROUP_CONCAT(`COLUMN_NAME` SEPARATOR ', ')
FROM `information_schema`.`COLUMNS`
WHERE (`TABLE_SCHEMA` = 'dbName')
AND (`TABLE_NAME` = 'some_stuff')
AND (`COLUMN_KEY` = 'PRI');
OUTPUT:
+--------------------------------------------+
| GROUP_CONCAT(`COLUMN_NAME` SEPARATOR ', ') |
+--------------------------------------------+
| firstID, secondID |
+--------------------------------------------+
SAP does, indeed do this (http://help.sap.com/saphelp_nw04s/helpdata/en/fc/eb3a53358411d1829f0000e829fbfe/content.htm). SQL Server is also based on Sybase, and I don't think Sybase supported this functionality. There are many limitations on the syntax.
On a query on one table with a primary key, no explicit order by, and no where conditions, MySQL will generally return the results in primary key order. You cannot depend on this functionality, but it might be good enough for your system.
The big issue would be the use of indexes for the where clause. If there are no indexes on the table, you don't have to worry about it. If there are, you could possibly emulate the behavior with a materialized view:
select t.*
from (select t.*
from table t
) t
where <where clause here>;
Another option is to force the database engine to use the primary key index. You can do this by using a force index hint. The issue is that you need to know the name of the index.
Related
What's the most efficient way of truncating the names in a first_name and last_name column to 1 character?
I have a mysql database that I want to hand off to a developer but I want to partially sanitize the data so that the two name records drop down to just initials.
I tried to modify the varchar to 1 character but mysql will not let me truncate the data, it just throws an error. I am working from a dump of the database before I hand it off. I want to obscure the names without making them all the same.
update tablename set first_name=left(first_name, 1), last_name = left(last_name, 1)
But, as Gordon Linoff mentioned, this could be expensive if you have a lot of rows in the table
Updating all the rows is rather expensive because of the logging required. The most efficient way is probably to create a new table:
create table for_developer as
select left(first_name, 1) as first_name, left(last_name, 1) as last_name,
. . . -- rest of columns
from t;
Why don't you try:
Update "you_table"
SET first_name = LEFT(first_name,1),
last_name = LEFT(last_name,1);
You could use like when creating a new table so you have the same column attribute and definitions as the old table. Be sure to check the manual on the behavior of like when creating tables though, especially if you are concerned about indexing.
create table new_table like old_table;
insert into new_table
select left(first_name, 1) as first_name, left(last_name, 1) as last_name, ..-- rest of columns
from old_table;
I need a query that could drop primary key only if it exists.
ALTER TABLE tablename DROP PRIMARY KEY;
This will return error if it does not exists, but my requirement is to run a query in different databases.
In MariaDB 10.2.16 i was able to solve this problem with:
ALTER TABLE tablename DROP INDEX IF EXISTS `PRIMARY`;
This should work with any table since the primary keys in MySQL are always called PRIMARY as stated in MySQL doc:
The name of a PRIMARY KEY is always PRIMARY, which thus cannot be
used as the name for any other kind of index.
I would recommend using this:
SELECT CONCAT('ALTER TABLE ', TABLE_SCHEMA, '.',TABLE_NAME,
' DROP PRIMARY KEY; ANALYZE TABLE ', TABLE_SCHEMA, '.',TABLE_NAME, ';')
FROM information_schema.COLUMNS
WHERE CONCAT(TABLE_SCHEMA,TABLE_NAME,COLUMN_NAME) IN
(SELECT CONCAT(TABLE_SCHEMA,TABLE_NAME,COLUMN_NAME)
FROM INFORMATION_SCHEMA.STATISTICS
WHERE INDEX_NAME = 'PRIMARY' -- *Required* to get only the primary keys from the statistics table.
-- *Optional*
AND TABLE_SCHEMA = 'clients_database');
Run this to generate your required SQL.
Copy your results, then run them as working queries.
ANALYZE TABLE is optional as well as the WHERE clause.
You can remove ANALYZE TABLE ', TABLE_SCHEMA, '.',TABLE_NAME, ';' if desired from the query below.
I exploit the information_schema when researching and utilizing standardization techniques.
Just about everything you would ever need or want to know about your tables and columns lives in some System table in either (if applicable)
Database / Table_schema:
information_schema
performance_schema
mysql
Note: From doc's
Internal schemas, such as "performance_schema", "information"schema", "sys", and "mysql", are hidden by default. Toggle the Show Metadata and Internal Schemas preference to list them in the object browser. Schemas beginning with a "." are also controlled by this setting.
NOTE: Here's something similar that has been created.
Hope this helps!
Cheers,
Jay
I think the easy option might be this:
first go to :
'YourDatabase'>tables>your table name>keys>copy the constraints like 'PK__TableName__0001'
then run this:
Query:alter Table 'TableName' drop constraint PK__TableName__0001
I have a MySQL DB with two columns. 'Key' and 'Used'. Key is a string, Used is an integer. Is there a very fast way to search for a specific Key and then return the Use in a huge MySQL DB with 6000000 rows of data.
You can make it fast by creating an index on key field:
CREATE INDEX mytable_key_idx ON mytable (`key`);
You can actually make it even faster for reading by creating covering index on both (key, used) fields:
CREATE INDEX mytable_key_used_idx ON mytable (`key`, `used`);
In this case, when reading, MySQL could retrieve used value from the index itself, without reading the table (index-only scan). However, if you have a lot of write activity, covering index may work slower because now it has to update both an index and actual table.
The normative SQL for that would be:
SELECT t.key, t.used FROM mytable t WHERE t.key = 'particularvalue' ;
The output from
EXPLAIN
SELECT t.key, t.used FROM mytable t WHERE t.key = 'particularvalue' ;
Would give details about the access plan, what indexes are being considered, etc.
The output from a
SHOW CREATE TABLE mytable ;
would give information about the table, the engine being used and the available indexes, as well as the datatypes.
Slow performance on a query like this is usually indicative of a suboptimal access plan, either because suitable indexes are not available, or not being used. Sometimes, a characterset mismatch between the column datatype and the literal datatype in the predicate can make an index "unusable" by a particular query.
I'm working on a event tracking system which uses a handful of lookup tables as well as the primary logging table. In a report I'm writing, an object can be selected to view statistics against. The interface shows all objects in order of decreasing importance (ie, hits).
The schema for the two tables (slightly trimmed down, but you get the gist):
CREATE TABLE IF NOT EXISTS `event_log` (
`event_id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(5) DEFAULT NULL,
`object_id` int(5) DEFAULT NULL,
`event_date` datetime DEFAULT NULL,
PRIMARY KEY (`event_id`),
KEY `user_id` (`user_id`),
KEY `object_id` (`object_id`)
);
CREATE TABLE IF NOT EXISTS `lookup_event_objects` (
`object_id` int(11) NOT NULL AUTO_INCREMENT,
`object_desc` varchar(255) NOT NULL,
PRIMARY KEY (`object_id`)
);
The query I'm having trouble with is below. It works fine with my table of ~100 entries, but the EXPLAIN worries me a little.
explain SELECT
el.object_id,
leo.object_desc,
COUNT(el.object_id) as count_rows
FROM
event_log el
LEFT JOIN lookup_event_objects leo ON leo.object_id = el.object_id
GROUP BY
el.object_id
ORDER BY
count_rows DESC,
leo.object_desc ASC
Returns:
Using index; Using temporary; Using filesort
So -- what's wrong with my schema and/or query for MySQL to fall back on temporary and filesort? Or is it as optimized as it can get using ORDER BY?
Well, the doc gives the exact reasons when "Using temporary" will appear:
Temporary tables can be created under conditions such as these:
If there is an ORDER BY clause and a different GROUP BY clause, or if
the ORDER BY or GROUP BY contains columns from tables other than the
first table in the join queue, a temporary table is created.
DISTINCT combined with ORDER BY may require a temporary table.
If you use the SQL_SMALL_RESULT option, MySQL uses an in-memory
temporary table, unless the query also contains elements (described
later) that require on-disk storage.
A quick scan shows that you suffer from #1.
And this blog from 2009 says that "using filesort" means that the sort can't be performed with an index. Since you're ordering by a computed field, that's going to be true, too.
So, that's what's "wrong".
Updated for MySQL 5.7 (src):
The server creates temporary tables under conditions such as these:
Evaluation of UNION statements, with some exceptions described later.
Evaluation of some views, such those that use the TEMPTABLE algorithm, UNION, or aggregation.
Evaluation of derived tables (subqueries in the FROM clause).
Tables created for subquery or semi-join materialization (see Section 8.2.2, “Optimizing Subqueries, Derived Tables, and View References”).
Evaluation of statements that contain an ORDER BY clause and a different GROUP BY clause, or for which the ORDER BY or GROUP BY contains columns from tables other than the first table in the join queue.
Evaluation of DISTINCT combined with ORDER BY may require a temporary table.
For queries that use the SQL_SMALL_RESULT modifier, MySQL uses an in-memory temporary table, unless the query also contains elements (described later) that require on-disk storage.
To evaluate INSERT ... SELECT statements that select from and insert into the same table, MySQL creates an internal temporary table to hold the rows from the SELECT, then inserts those rows into the target table. See Section 13.2.5.1, “INSERT ... SELECT Syntax”.
Evaluation of multiple-table UPDATE statements.
Evaluation of GROUP_CONCAT() or COUNT(DISTINCT) expressions.
These are the following conditions under which temporary tables are created.
UNION queries use temporary tables.
Some views require temporary tables, such those evaluated using the TEMPTABLE algorithm, or that use UNION or aggregation.
If there is an ORDER BY clause and a different GROUP BY clause, or if the ORDER BY or GROUP BY contains columns from tables other than the first table in the join queue, a temporary table is created.
DISTINCT combined with ORDER BY may require a temporary table.
If you use the SQL_SMALL_RESULT option, MySQL uses an in-memory temporary table, unless the query also contains elements (described later) that require on-disk storage.
Follow this link by mysql:
http://dev.mysql.com/doc/refman/5.1/en/internal-temporary-tables.html
I have a simple query like:
select count(*) from table
Can I speed up MySQL by doing:
select count(id) from table
Does this make any difference in terms of speed?
Best Regards,
Not if id is your primary key. * will simply map to your primary key. count(id) = count(*)
You can try to get the count from information_schema
select TABLE_ROWS from information_schema.tables
where TABLE_SCHEMA = '$db' and TABLE_NAME = '$tbl';
If id is your primary key, then no it will not, since MySql will use the primary key under the covers anyway. If id is not the primary key, then it may actually make the query slightly slower.
If id is "not null", no it won't make any difference.
If id is nullable, then it is a different query and will not (always) produce the same result.