Finding duplicate values over multiple databases in MySQL - mysql

There are multiple (mysql) databases running under 1 website. 1 database per registered account.
I'm looking for duplicate records in each contacts-table.
This is what i have for finding duplicates inside only 1 database:
SELECT COUNT(contacts.email), contacts.email, contacts.state, contacts.source,
FROM shard_40000006.contacts
LEFT JOIN shard_40000006.optin ON optin.email=contacts.email
GROUP BY contacts.email
HAVING COUNT(contacts.email) > 1;
The query i need searches for duplicates over shard_40000001, shard_40000002, shard_40000003, .. shard_40999999

You can use dynamic tsql.
declare #sqltext nvarchar(max) = N''
declare #parameter varchar(max) = 'define what you want to search here'
select #sqltext += '
SELECT COUNT(contacts.email), contacts.email, contacts.state, contacts.source,
FROM '+name+'.dbo.contacts
LEFT JOIN '+name+'.dbo.optin ON optin.email=contacts.email
GROUP BY contacts.email
HAVING COUNT(contacts.email) > 1'
FROM sys.databases
WHERE OBJECT_ID(QUOTENAME(name) + '.dbo.prac_nag', 'U') IS NOT NULL -- will return only if the table exists
print #sqltext
-- once your review the output, uncomment out below
--exec sp_executesql #sqltext
I just put refernce code. i didn't test this code.

Related

Is it possible that I could find a row contains a string? Assume that I do not know which columns contain a string

I know that there are several ways to find which row's column contains a string, like using [column name] regexp ' ' or [column name] like ' '
while currently what I need some help is I have a table with several columns, all of there are varchar or text and I am not sure which column contains a certain string. Just say that I want to search a "xxx from a table. Several different columns could contain this string or not. Is there a way that I could find which column contains this string?
I have a thinking and the solution could be
select * from [table name] where [column1] regexp 'xxx' or
[column2] regexp 'xxx' or ...... [column39] regexp 'xxx' or .....
[colum60] regexp 'xxx' or ... or [column 80] regexp 'xxx';
I do not want the query like this. Is there another effective way?
To give a better example, say that we are searching for a table that belongs to a blog.
We have title, URL, content, key words, tag, comment and so on. Now we just say, if any blog article is related to "database-normalization", this word may appear in the title, URL or content or anywhere, and I do not want to write it one by one like
where title regexp 'database-normalization' or content regexp 'database-normalization' or url regexp 'database-normalization'......
as when there are hundreds columns, I need to write a hundred, or in this case is there an effective way instead of write hundred or statement? Like using if-else or collections or some others to build the query.
If you want a pure dynamic way, you can try this. I've tried it long back on sql-server and hope it may help you.
#TMP_TABLE -- a temporary table
- PK, IDENTITY
- TABLE_NAME
- COLUMN_NAME
- IS_EXIST
INSERT INTO #TMP_TABLE (TABLE_NAME,COLUMN_NAME)
SELECT C.TABLE_NAME, COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS C
WHERE C.TABLE_NAME = <your-table> AND C.DATA_TYPE = 'varchar'; -- you can modify it to handle multiple table at once.
-- boundaries
SET #MINID = (SELECT ISNULL(MIN(<PK>),0) FROM #TMP_TABLE );
SET #MAXID = (SELECT ISNULL(MAX(<PK>),0) FROM #TMP_TABLE );
WHILE ((#MINID<=#MAXID) AND (#MINID<>0))
BEGIN
SELECT #TABLE_NAME = TABLE_NAME,#COLUMN_NAME = COLUMN_NAME
FROM #TMP_TABLE
WHERE <PK> = #MINID;
SET #sqlString = ' UPDATE #TMP_TABLE
SET IS_EXIST = 1
WHERE EXIST (SELECT 1 FROM '+ #TABLE_NAME+' WHERE '+ #COLUMN_NAME +' = ''demo.webstater.com'') AND <PK> = '+ #MINID;
EXEC(#sql) ;
SET #MINID = (SELECT MIN(<PK>) FROM #TMP_TABLE WHERE <PK> > #MINID );
END
SELECT * FROM #TMP_TABLE WHERE IS_EXIST = 1 ; -- will give you matched results.
If you know the columns in advance, what you proposed is probably the most effective way (if a little verbose).
Otherwise, you could get the column names from INFORMATION_SCHEMA.COLUMNS and construct dynamic SQL based on that.
His question is not to query specific columns with like clause. He has been asking to apply same pattern across columns dynamically.
Example: Table having 3 columns - FirstName, LastName, Address and pattern matching is "starts with A" then resulting query should be:
Select * From Customer where FirstName like 'A%" or LastName like 'A%" or Address like 'A%'
If you want to build query in business layer, this could easily be done with reflection along with EF.
If you are motivated to do in database then you can achieve by building query dynamically and then execute through sp_executesql.
Try this (Just pass tablename and the string to be find)-:
create proc usp_findString
#tablename varchar(500),
#string varchar(max)
as
Begin
Declare #sql2 varchar(max),#sql nvarchar(max)
SELECT #sql2=
STUFF((SELECT ', case when '+QUOTENAME(NAME)+'='''+#string+''' then 1 else 0 end as '+NAME
FROM (select a.name from sys.columns a join sys.tables b on a.[object_id]=b.[object_id] where b.name=#tablename) T1
--WHERE T1.ID=T2.ID
FOR XML PATH('')),1,1,'')
--print #string
set #sql='select '+#sql2+' from '+#tablename
print #sql
EXEC sp_executesql #sql
End
SQL Server 2014
One way is to use CASE to check the substring existence with LOCATE in mysql and return the column but all you have to check in every column of the table as below:
CREATE TABLE test(col1 VARCHAR(1000), col2 VARCHAR(1000), col3 VARCHAR(1000))
INSERT INTO test VALUES
('while currently what I need some help is I have a table with 10 columns',
'contains a certain string. Just say that I want to search a table',
'contains a certain string demo.webstater.com')
SELECT (CASE WHEN LOCATE('demo.webstater.com', col1, 1) > 0 THEN 'col1'
WHEN LOCATE('demo.webstater.com', col2, 1) > 0 THEN 'col2'
WHEN LOCATE('demo.webstater.com', col3, 1) > 0 THEN 'col3'
END) whichColumn
FROM test
OUTPUT:
whichColumn
col3
There are many ways in which you can do your analysis. You can use "LIKE A%%" if it starts from A in SQL, "REGEX" LibrarY for multiple checks.

How to loop through all the tables on a database to update columns

I'm trying to update a column (in this case, a date) that is present on most of the tables on my database. Sadly, my database has more than 100 tables already created and full of information. Is there any way to loop through them and just use:
UPDATE SET date = '2016-04-20' WHERE name = 'Example'
on the loop?
One painless option would be to create a query which generates the UPDATE statements you want to run on all the tables:
SELECT CONCAT('UPDATE ', a.table_name, ' SET date = "2016-04-20" WHERE name = "Example";')
FROM information_schema.tables a
WHERE a.table_schema = 'YourDBNameHere'
You can copy the output from this query, paste it in the query editor, and run it.
Update:
As #PaulSpiegel pointed out, the above solution might be inconvenient if one be using an editor such as HeidiSQL, because it would require manually copying each record in the result set. Employing a trick using GROUP_CONCAT() would give a single string containing every desired UPDATE query in it:
SELECT GROUP_CONCAT(t.query SEPARATOR '; ')
FROM
(
SELECT CONCAT('UPDATE ', a.table_name,
' SET date = "2016-04-20" WHERE name = "Example";') AS query,
'1' AS id
FROM information_schema.tables a
WHERE a.table_schema = 'YourDBNameHere'
) t
GROUP BY t.id
You can use SHOW TABLES command to list all tables in database. Next you can check if column presented in table with SHOW COLUMNS command. It can be used this way:
SHOW COLUMNS FROM `table_name` LIKE `column_name`
If this query returns result, then column exists and you can perform UPDATE query on it.
Update
You can check this procedure on sqlfiddle.
CREATE PROCEDURE UpdateTables (IN WhereColumn VARCHAR(10),
IN WhereValue VARCHAR(10),
IN UpdateColumn VARCHAR(10),
IN UpdateValue VARCHAR(10))
BEGIN
DECLARE Finished BOOL DEFAULT FALSE;
DECLARE TableName VARCHAR(10);
DECLARE TablesCursor CURSOR FOR
SELECT c1.TABLE_NAME
FROM INFORMATION_SCHEMA.COLUMNS c1
JOIN INFORMATION_SCHEMA.COLUMNS c2 ON (c1.TABLE_SCHEMA = c2.TABLE_SCHEMA AND c1.TABLE_NAME = c2.TABLE_NAME)
WHERE c1.TABLE_SCHEMA = DATABASE()
AND c1.COLUMN_NAME = WhereColumn
AND c2.COLUMN_NAME = UpdateColumn;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET Finished = TRUE;
OPEN TablesCursor;
MainLoop: LOOP
FETCH TablesCursor INTO TableName;
IF Finished THEN
LEAVE MainLoop;
END IF;
SET #queryText = CONCAT('UPDATE ', TableName, ' SET ', UpdateColumn, '=', QUOTE(UpdateValue), ' WHERE ', WhereColumn, '=', QUOTE(WhereValue));
PREPARE updateQuery FROM #queryText;
EXECUTE updateQuery;
DEALLOCATE PREPARE updateQuery;
END LOOP;
CLOSE TablesCursor;
END
This is just an example how to iterate through all tables in database and perform some action with them. Procedure can be changed according to your needs.
Assuming you are using MySQL, You can use Stored Procedure.
This post is a very helpful.
Mysql-loop-through-tables

mysql concatenated string with row value as column name for every row in select query (only by query)

Below is my table TABLE
id colname1 colname2 colname3
1 Alex John Mary
2 Alyssa Eben Stephen
3 Sandra Tina William
I try to use below query
SELECT * FROM TABLE WHERE CONCAT('colname',id) = 'Eben'
I expected the result would be from 2nd row 2nd column. But I get nothing. I referred many solutions which guides to use GROUP_CONCAT but I get nothing worked.
Is this possible to do this with mysql?
You can try dynamic sql. Populate a string variable with your select such as:
DECLARE #str varchar(50) = 'SELECT * FROM TABLE WHERE colname#id = ''' + 'Eben' + ''''
Then replace the placeholder string ("#id") with the column number you want such as:
select REPLACE(#str, '#id', 2)
Last to actually execute the statement simply use "EXEC " + statement. ie:
EXEC (#str)
I had to create another answer because this forum won't allow me to use the #(ampersand) in comments.
DECLARE #str varchar(50) = 'SELECT * FROM TABLE WHERE colname#id = ''' +
'EBEN' + ''''
select #str = REPLACE(#str, '#id', 2)
select #str '#str' --This is just to examine the code, get the results of
--this and run it to test
--you should get this: SELECT * FROM TABLE WHERE colname2 = 'EBEN'
EXEC (#str) --this will actually execute your code

"Looping" through databases with sp_MSforeachdb and returning 1 data set

So, I've been wrestling with the code I found on my buddy's website:
8 Steps to Moving Database Logins
I want to generate the Database Level Security, Roles, and Explicit Permissions statements in one output so I'm not copying and pasting over and over again and so that they run for all databases on the server (minus tempdb of course).
Declare #FullStatement varchar(MAX)
Set #FullStatement = ' use [?]; SELECT dp.state_desc + N'' '' + dp.permission_name + N'' TO '' + cast(QUOTENAME(dpl.name COLLATE DATABASE_DEFAULT) as nvarchar(500)) AS TSQLScript
FROM sys.database_permissions AS dp
INNER JOIN sys.database_principals AS dpl ON (dp.grantee_principal_id = dpl.principal_id)
WHERE dp.major_id = 0
and dpl.name not like ''##%'' -- excluds PBM accounts
and dpl.name not in (''dbo'', ''sa'', ''public'')
ORDER BY dp.permission_name ASC, dp.state_desc ASC'
Exec sp_MSforeachdb #FullStatement
How can I modify what I have, which works as is but is inconvenient, using a Table Variable, Temp Table, etc so all of the statements are in one data set?
David,
Is this what you want?
CREATE TABLE tempdb.dbo.Results (c1 VARCHAR(8000))
Declare #FullStatement varchar(MAX)
Set #FullStatement = 'SELECT ''use [?]''; SELECT dp.state_desc + N'' '' + dp.permission_name + N'' TO '' + cast(QUOTENAME(dpl.name COLLATE DATABASE_DEFAULT) as nvarchar(500)) AS TSQLScript
FROM [?].sys.database_permissions AS dp
INNER JOIN [?].sys.database_principals AS dpl ON (dp.grantee_principal_id = dpl.principal_id)
WHERE dp.major_id = 0
and dpl.name not like ''##%'' -- excluds PBM accounts
and dpl.name not in (''dbo'', ''sa'', ''public'')
ORDER BY dp.permission_name ASC, dp.state_desc ASC'
INSERT INTO tempdb.dbo.Results Exec sp_MSforeachdb #FullStatement
select * FROM tempdb.dbo.Results
There are multiple ways to get this done. You can use powershell to loop through all databases and put the results in excel. Mr Nelson has this on his powershell sql university series. Sorry, would pull the link for you but i am typing on my phone at the airport.
Why are you scripting database users and permissions? They are in the database and will still be there when you migrate the databases. You don't need to do that unless you're recreating the databases from scratch.

How to dynamically write the query in SQL Server 2008?

How to write the dynamically the below query?
Table
empid designation interestes
1 developer,tester cricket,chess
1 developer chess
1 techlead cricket
Condition:
IF empid = 1
AND (designation LIKE '%developer%' OR designationLIKE '%techlead%')
OR (interests LIKE '%cricket%').
How to write the above query dynamically if designations need to send more than 2,and also same on interstes .
please tell me ...
EDIT stored procedure code:
ALTER PROCEDURE [dbo].[usp_GetDevices]
#id INT,
#designation NVARCHAR (MAX)
AS
BEGIN
declare #idsplat varchar(MAX)
set #idsplat = #UserIds
create table #u1 (id1 varchar(MAX))
set #idsplat = 'insert #u1 select ' + replace(#idsplat, ',', ' union select ')
exec(#idsplat)
Select
id FROM dbo.DevicesList WHERE id=#id AND designation IN (select id1 from #u1)
END
Then when your form is submitted, create a string of designations (should really be a list of foreign keys if you have a 1 to many relationship) and pass that to the SQL. Then parse it into a table using one of many open-source SQL user functions:
-- #designations = 'developer,tester,techlead'
select text_val
from dbo.fn_ParseText2Table(#designations,',')
/* results:
text_val
--------
developer
tester
techlead
*/
Once you have the values in a table you can do any standard join or query operations.