I have more than 100 tables in SQL which don't have primary keys or indexes on the columns.
I'm manually checking each column for each table to see whether they have unique keys or not.
How do I query to get unique key columns if they are not present in the table?
Here You Need To Find Column Name Belongs To Particular Table:
SELECT c.name AS ColName, t.name AS TableName
FROM sys.columns c
JOIN sys.tables t ON c.object_id = t.object_id
WHERE c.name LIKE '%write here column name%';
Please try following query
select stat.table_schema as database_name,
stat.table_name,
stat.index_name,
group_concat(stat.column_name
order by stat.seq_in_index separator ', ') as columns,
tco.constraint_type
from information_schema.statistics stat
join information_schema.table_constraints tco
on stat.table_schema = tco.table_schema
and stat.table_name = tco.table_name
and stat.index_name = tco.constraint_name
where stat.non_unique = 0
and stat.table_schema not in ('information_schema', 'sys',
'performance_schema', 'mysql')
and (tco.constraint_type !='UNIQUE' OR tco.constraint_type !='PRIMARY KEY') //You can made changes here if needed
group by stat.table_schema,
stat.table_name,
stat.index_name,
tco.constraint_type
order by stat.table_schema,
stat.table_name;
you may want to try something like this
SELECT schema_name(t.schema_id), t.name, i.name
FROM sys.indexes i
INNER JOIN sys.tables t ON t.object_id= i.object_id
WHERE i.type>0 and t.is_ms_shipped=0 and t.name<>'sysdiagrams'
and (is_unique_constraint=1)
Courtesy: This link
Related
I am new to performance tuning in MySQL & need your help concerning a view that will replace a table later-on in our design.
The table to be replaced is called users and has the following attributes:
The users2 view has the following attributes:
When I execute a normal SELECT on both objects, they respond at the same time:
SELECT *
FROM `users`
SELECT *
FROM `users2`
But an ordered version of these queries result in a different performance: The table is a little slower (takes less than two seconds), the view need about ten times this time:
SELECT *
FROM `users`
ORDER BY `lastName`, `firstName`
SELECT *
FROM `users2`
ORDER BY `lastName`, `firstName`
To find out the reason, I let EXPLAIN the two comments:
Obviously, an ALL on table 'a' (addresses) on the attribute Countries_ID is making trouble, so I made the following:
ALTER TABLE addresses ADD INDEX (Countries_ID);
This index didn't change anything at all. So, I ask you for your opinion what can be done better.
Notice 1: Is there a way to create an index on temporary column Countries_ID_2?
Notice 2: The users2 view was created with the following SQL query:
CREATE OR REPLACE VIEW users2 AS
(SELECT p.username
, p.password
, p.firstName
, p.lastName
, p.eMail AS email
, a.settlement AS city
, s.name AS country
, pl.languages
, p.description
, p.ID AS ID
, p.phone1
, p.phone2
, CONCAT_WS(' ', a.street, a.addition) AS address
, p.status
, p.publicMail
, ad.name AS Betreuer
FROM addresses a
INNER JOIN addresses_have_persons ap ON a.ID = ap.Addresses_ID
INNER JOIN countries c ON a.Countries_ID = c.ID
INNER JOIN persons p ON a.ID = p.addressID
AND ap.Persons_ID = p.ID
INNER JOIN states s ON a.States_ID = s.ID
INNER JOIN persons_language pl ON p.ID = pl.ID
LEFT JOIN advisors ad ON p.advisorID = ad.ID
-- LEFT JOIN titles t ON t.ID = ad.titleID
);
Notice 3: Although a lot of fields in the persons table are NULL, there is not a single row where these fields are altogether NULL.
EDIT:
CREATE OR REPLACE VIEW persons_language AS
(SELECT lp.Persons_ID AS ID
, GROUP_CONCAT(DISTINCT l.name ORDER BY l.name SEPARATOR ', ') AS languages
FROM languages l
, languages_have_persons lp
WHERE l.ID = lp.Languages_ID
GROUP BY lp.Persons_ID);
Without the ORDER BY, the language names are not alphabetically ordered, which I currently want. Perhaps, we could decide to get them in any order, but we'll see.
Currently, I made the following modifications without any performance improvement:
ALTER TABLE addresses ADD INDEX (Countries_ID);
ALTER TABLE addresses ADD INDEX (States_ID);
ALTER TABLE addresses_have_persons ADD INDEX (Addresses_ID);
ALTER TABLE languages ADD INDEX (name);
ALTER TABLE persons ADD INDEX (addressID);
ALTER TABLE persons ADD INDEX (address2ID);
ALTER TABLE persons ADD INDEX (address3ID);
ALTER TABLE persons ADD INDEX (advisorID);
EDIT 2:
I discuss this issue also on another site. The discussions there let me do the following changes to be nearer to the third normal form:
CREATE OR REPLACE TABLE accounts
(ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY
, username VARCHAR(50) NOT NULL UNIQUE
, password VARCHAR(255) NOT NULL
, eMail VARCHAR(100) NOT NULL
, Persons_ID INT NOT NULL
);
INSERT INTO accounts (username, password, eMail, Persons_ID)
SELECT username, password, eMail, ID
FROM persons;
The table persons does contain only the most necessary things and has the following structure now:
The new table persons_information carries all additional information:
I recreated the users2 with the following command:
CREATE OR REPLACE VIEW users2 AS
(SELECT ac.username
, ac.password
, p.firstName
, p.lastName
, ac.eMail AS email
, adr.settlement AS city
, s.name AS country
, pl.languages
, pi.description
, ac.Persons_ID AS ID
, pi.phone1
, pi.phone2
, CONCAT_WS(' ', adr.street, adr.addition) AS address
, p.status
, pi.publicMail
, adv.name AS Betreuer
FROM accounts ac
INNER JOIN persons p ON ac.Persons_ID = p.ID
INNER JOIN persons_information pi ON p.ID = pi.ID
INNER JOIN addresses adr ON adr.ID = pi.addressID
INNER JOIN addresses_have_persons ap ON adr.ID = ap.Addresses_ID
AND ap.Persons_ID = p.ID
INNER JOIN countries c ON adr.Countries_ID = c.ID
INNER JOIN states s ON adr.States_ID = s.ID
INNER JOIN persons_language pl ON p.ID = pl.ID
LEFT JOIN advisors adv ON pi.advisorID = adv.ID
-- LEFT JOIN titles t ON t.ID = adv.titleID
);
The SELECT _ FROM users2 is fast, but if I add an ORDER BY lastName, firstName, it takes about 25 seconds to get the response.
Here are the results of the *EXPLAIN SELECT * FROM users2* command:
And here for the other command:
I also (re)created following indexes:
ALTER TABLE addresses ADD INDEX (Countries_ID);
ALTER TABLE addresses ADD INDEX (States_ID);
ALTER TABLE addresses_have_persons ADD INDEX (Persons_ID);
ALTER TABLE languages ADD INDEX (name);
ALTER TABLE persons_information ADD INDEX (addressID);
ALTER TABLE persons_information ADD INDEX (address2ID);
ALTER TABLE persons_information ADD INDEX (address3ID);
ALTER TABLE persons_information ADD INDEX (advisorID);
I think one reason for the problem is the persons_language view that is created as follows:
CREATE OR REPLACE VIEW persons_language AS
(SELECT lp.Persons_ID AS ID
, GROUP_CONCAT(DISTINCT l.name ORDER BY l.name SEPARATOR ', ') AS languages
FROM languages l
INNER JOIN languages_have_persons lp ON l.ID = lp.Languages_ID
GROUP BY lp.Persons_ID);
EDIT 3:
For those interested, I add the EXPLAIN for the persons_language view:
EDIT 4:
After the database meeting today, we decided to delete all objects related to the address information & recreated the view with
CREATE OR REPLACE VIEW `users2` AS
(SELECT ac.username
, ac.password
, p.firstName
, p.lastName
, ac.eMail AS email
, pl.languages
, pi.description
, ac.Persons_ID AS ID
, pi.phone1
, pi.phone2
, p.status
, pi.publicMail
, adv.name AS Betreuer
FROM accounts ac
INNER JOIN persons p ON ac.Persons_ID = p.ID
INNER JOIN persons_information pi ON p.ID = pi.ID
INNER JOIN persons_language pl ON p.ID = pl.ID
INNER JOIN advisors adv ON pi.advisorID = adv.ID
WHERE ac.password IS NOT NULL
);
I also created an index with
CREATE INDEX LanguagesPersonsIndex ON `languages_have_persons` (`Languages_ID`, `Persons_ID`);
The EXPLAIN command shows that the new indices are in use and that the delay after a SELECT with an ORDER BY clause with the new, smaller view is about 18 s. Here is the new result:
My question is: What could I do more to improve the performance?
The key fault must be the problem.
But depending on data volume on the joined tables, it'll anyway be slower.
Try To:
Implement KeyIndexes on ALL attributes used to stablish relationships. (ap.Addresses_ID, a.Countries_ID, p.addressID, ap.Persons_ID, a.States_ID, p.advisorID).
Declare PK on All 'ID' columns.
Don't use ORDER or GROUP in the views construction.
Declare Key Index for attributes that are most used on searches, ordering or grouping.
Tip: The 'INNER' (INNER JOIN) isn't necessary. Is the same of 'JOIN'
Your VIEW "persons_language" would be better like this:
SELECT lp.Persons_ID AS ID, GROUP_CONCAT(DISTINCT l.name ORDER BY l.name SEPARATOR ', ') AS languages
FROM languages_have_persons lp
JOIN languages l ON l.ID = lp.Languages_ID
GROUP BY lp.Persons_ID;
It's more appropriate because the clauses 'FROM' and 'JOIN' are processed before 'WHERE' clause.
You may boost your mysql memory and cache configurations.
Look the my mysql server's configurations (Runs an ERP with weight tables and views):
join_buffer_size= 256M
key_buffer = 312M
key_buffer_size = 768M
max_allowed_packet = 160M
thread_stack = 192K
thread_cache_size = 8
query_cache_limit = 64M
innodb_buffer_pool_size = 1512M
table_cache = 1024M
read_buffer_size = 4M
query_cache_size = 768M
query_cache_limit = 128M
SELECT *
FROM `users`
ORDER BY `lastName`, `firstName`
needs
INDEX(last_name, first_name) -- in that order
Beware of VIEWs; some VIEWs optimize well, some do not.
Please provide SHOW CREATE TABLE for both addresses and addresses_have_persons.
In persons_language, why do you need DISTINCT? Doesn't it have PRIMARY KEY(person, language) (or in the opposite order)? Let's see SHOW CREATE TABLE.
Please provide the EXPLAIN for any query you want to discuss.
how to increase the performance of this mysql query
SELECT '' AS sharedid,
hubber_posts.userID AS postowner,
hubber_posts.*,
'' AS sharedby,
hubber_posts.userID AS userID,
hubber_posts.posted_date AS DATE,
'' AS sharebyusr,
'' AS sharebyusrimg,
Concat_ws(' ', firstname, lastname) AS fullname,
username AS postedBy,
hubber_user.image,
hubber_user.gender AS gender,
(SELECT accounttype
FROM hubber_user_security us
WHERE hubber_user.ID = us.userID
AND hubber_posts.userID = us.userID) AS accounttype,
'' AS sharebyusrtype
FROM hubber_posts
INNER JOIN hubber_user
ON hubber_posts.userID = hubber_user.ID
WHERE hubber_posts.status = 1
Your example code has a correlated subquery. MySQL performs poorly with those, as of late 2016.
Try converting it to a JOINed table.
SELECT all that stuff,
us.accounttype
FROM hubber_posts
JOIN hubber_user ON hubber_posts.userID = hubber_user.ID
LEFT JOIN hubber_user_security us ON hubber_user.ID = us.userID
WHERE hubber_posts.status = 1
I used LEFT JOIN. Without the LEFT, any rows without a matching entry in that table will be suppressed from the result set.
You query is essentially this:
SELECT . . .
(SELECT accounttype
FROM hubber_user_security us
WHERE u.ID = us.userID AND
p.userID = us.userID
) AS accounttype,
. . .
FROM hubber_posts p INNER JOIN
hubber_user u
ON p.userID = u.ID
WHERE p.status = 1 ;
For this query, the optimal indexes are:
hubber_posts(status, userId)
hubber_user(Id)
hubber_user_security(userId)
I would note that the subquery has an extra correlation condition that is not necessary -- the user ids are already equal. And, you run the risk of getting an error if there are multiple account types.
You may intend:
(SELECT GROUP_CONCAT(accounttype)
FROM hubber_user_security us
WHERE u.ID = us.userID
) as accounttypes,
My suggestion is to support a join where hubber_posts is the base table and the 2 other tables are joined using nested loops.
No need to index hubber_posts for the join.
hubber_user.ID should be a PK.
hubber_user_security.userID should be indexed (and defined as a FK references hubber_user.ID).
As for the WHERE clause - only if you have relatively few rows where hubber_posts.status = 1 then you should add an index on hubber_posts.status
P.s.
since the join contain the condition -
ON hubber_posts.userID = hubber_user.ID
There is no need to compare both hubber_posts.userID and hubber_user.ID to us.userID
I have two tables named :
url_alias
product
url_alias TABLE has following fields : id, query, keyword
id is numeric,
query is of the form "product_id=45"
keyword is of the form "product-actual-name-in-url-friendly-manner"
product table has following fields: product_id, name, language_id
What I want is to update url_alias table "keyword" field with proper url friendly string which I have generated by using mysql REPLACE function and is aliased as NEW_KEYWORD but the url friendly string needs to be auto-generated from a join of both tables .
Following query shows a SELECT query on tables properly:
SELECT u.url_alias_id, u.query, u.keyword, p.name, REPLACE( p.name, ' ', '-' ) AS NEW_KEYWORD
FROM url_alias u, product p
WHERE u.query = CONCAT( "product_id=", p.product_id )
AND p.language_id =3
Please help me in an update query by using this query
Try it:
UPDATE url_alias u
JOIN product p
ON u.query = CONCAT( "product_id=", p.product_id )
SET u.keyword=REPLACE( p.name, ' ', '-' )
WHERE p.language_id =3;
I have the following query:
SELECT
issue.`sequence` AS issue_sequence,
issue.`description` AS issue_description,
GROUP_CONCAT(DISTINCT(issue_category.`name`) SEPARATOR ', ') AS issue_category_name,
GROUP_CONCAT(DISTINCT(approach.`name`) SEPARATOR ', ') AS approach_name,
issue_approach.`issue_id` AS issue_approach_issue_id,
issue_approach.`approach_id` AS issue_approach_approach_id
FROM
`approach` approach
INNER JOIN `issue_approach` issue_approach ON approach.`id` = issue_approach.`approach_id`
INNER JOIN `issue` issue ON issue_approach.`issue_id` = issue.`id`
INNER JOIN `project` project ON issue.`project` = project.`id`
INNER JOIN `tenant` tenant ON project.`tenant_id` = tenant.`id`
INNER JOIN `issue_category` issue_category ON project.`id` = issue_category.`project`
INNER JOIN `user` user ON tenant.`id` = user.`tenant_id`
WHERE user.id = 1 AND project.id = 1
GROUP BY issue_category_name
ORDER BY issue_category.`name`, issue.`sequence`
I am having a problem with this line:
GROUP BY issue_category_name
Apparently, MySQL can't seem to Group by the alias for by GROUP_CONCAT result.
I am not really an expert with SQL, but is there a way I can group using the result of GROUP_CONCAT?
Sample data;
Categories: Network, Servers
Issue Id: 1
Description: Some description
Approaches: Some approaches to resolve the issue.
Basically, an issue can belong to one or many categories. I am concatenating categories for each issue. What i want to do is group the results by the result of concatenation of categories. So for example group issues whose categories are Network,Servers.
Thanks.
Im not a MySQL user, but change your group by to
Group By GROUP_CONCAT(DISTINCT(issue_category.`name`) SEPARATOR ', ')
With reference to SQL EXECUTION ORDER, the reason why this will not work is because the select statement is the last statement to be executed so that sql engine will not be knowing your column alias while grouping the records as GROUP BY occurs before SELECT. So that as Charles Bretana's answer suggests, put
Group By GROUP_CONCAT(DISTINCT(issue_category.`name`) SEPARATOR ', ')
in your group by clause. It will work fine then.
Hope this helps you.
I have been studying indexing using Adventureworks2008R2 and was told to run this query.
SELECT s.name AS SchemaName,
OBJECT_NAME(i.object_id) AS TableOrViewName,
i.name AS IndexName,
c.name AS ColumnName
FROM sys.indexes AS i
INNER JOIN sys.index_columns AS ic
ON i.object_id = ic.object_id
INNER JOIN sys.columns AS c
ON ic.object_id = c.object_id
AND ic.column_id = c.column_id
INNER JOIN sys.objects AS o
ON i.object_id = o.object_id
INNER JOIN sys.schemas AS s
ON o.schema_id = s.schema_id
WHERE ic.is_included_column <> 0
AND s.name <> 'sys'
ORDER BY SchemaName, TableOrViewName, i.index_id, ColumnName;
The output looks like this:
SchemaName TableorViewName IndexName ColumnName
1 Production Productreview PK_ProductReview_ProductReviewID Comments
2 Production Productreview IX_ ProductReview_ProductId_Name Comments
I understand why it lists IX_ ProductReview_ProductId_Name but cannot make out why it shows PK_ProductReview_ProductReviewID.
The notes say by way of explanation "All columns in a clustered index are, by definition, included already."
In which case why is only the Comments column shown and why aren't all PKs listed?
I suspect I am being reeeeally dumb but . . .
TIA
--Edit
It seeems that all PKs on a table that have a non clustered index with an included column are shown to have the same included column.
That is the "how" answered, but I would still like the "why" answered.
At least one of those joins conditions appears to be insufficient. Both sys.indexes and sys.index_columns can define multiple indexes for the same object_id.
Try:
FROM sys.indexes AS i
INNER JOIN sys.index_columns AS ic
ON i.object_id = ic.object_id AND
i.index_id = ic.index_id
In other words, I think one of the rows is a phantom produced by this incorrect join. You're seeing it because it happens to be another index on the same table.
And, by the way, a better way of filtering out system objects (usually) is to include OBJECTPROPERTY(object_id,N'IsMSShipped') = 0 in your WHERE clause, rather than querying the schema that an object belongs to.