SELECT not working only inside #sql statement - mysql

i've got a query that works perfectly on a table, and doesn't work on another:
Here's the code (please note that is part of another query):
SET #sql = Null;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT('SUM(CASE WHEN columnA = "' ,columnA, ' "THEN 1 ELSE 0 end) AS "' ,columnA, ' "'))
into #sql
from table;
I don't know why it works for a table, but when i switch on another table it gives me this error
1 row(s) affected, 1 warning(s): 1260 Row 12 was cut by GROUP_CONCAT()
IF i remove the #sql thing and i do the select it gives me the data without error...BUT it truncates the string at some point... i don't know why

Cause you are trying to build a dynamic query (prepared statement) which should be something like below. Again you CASE condition CASE WHEN columnA = columnA THEN 1 ELSE 0 end doesn't seems valid since columnA = columnA will always be true.
declare #sql varchar(200);
set #sql = 'SELECT
GROUP_CONCAT(DISTINCT
CONCAT(SUM(CASE WHEN columnA = columnA THEN 1 ELSE 0 end) AS columnA))
into (another sql query part)
from table';

group_concat() has a maximum length, and you seem to be exceeding that. This is documented here.
You can reset the maximum length.
But, you can also simplify your logic:
SET #sql = Null;
SELECT GROUP_CONCAT(DISTINCT REPLACE('SUM(ColumnA = ''#val'') as `#val`',
'#val', columnA))
into #sql
from table;
Without the case expression, perhaps your string will fit in the default length.
I find it much easier to use replace() for constructing strings rather than concat(). You can see the template that you are using.

Related

How to show columns under certain condition in mySQL

I am (new to) working in MySQL but so far I was getting along OK. Now I'm stuck on the following:
I want to show a column in the query result but only if a certain condition is met. If the condition is not met then I would like the column not to show up in the query result (rather than show an empty column with only NULL values).
I tried to use a CASE WHEN statement but in that case it always shows the column in the query result, even if it is empty.
The problem is that I need to do this for 12 columns. The main reason is to avoid the query result to be cluttered with useless empty columns. The result of the query is to be processed by other people in excel.
Thanks in advance for your kind replies.
Although not ideal, you could use a prepared statement to only select columns where some rows are not null.
Suppose you have this table test where c is null for all the rows:
a
b
c
1
1
null
2
null
null
null
3
null
You can do this:
SET #query = 'SELECT ';
SET #query = CASE WHEN (SELECT COUNT(a) FROM test) > 0 then CONCAT(#query, 'a,') ELSE #query END;
SET #query = CASE WHEN (SELECT COUNT(b) FROM test) > 0 then CONCAT(#query, 'b,') ELSE #query END;
SET #query = CASE WHEN (SELECT COUNT(c) FROM test) > 0 then CONCAT(#query, 'c,') ELSE #query END;
SET #query = CONCAT(TRIM(TRAILING ',' FROM #query), ' FROM test');
Then:
PREPARE statement FROM #query;
EXECUTE statement;
Output:
a
b
1
1
2
null
null
3
Fiddle

How to concatenate strings in a Select statement in MySQL

I have to run a query in SQL Server using data from a MySQL database. When I needed to do the opposite, I found an easy way to accomplish what I needed writing an update query using the select statement in SQL Server.
In SQL Server I wrote:
SELECT 'update sgidb.Example set MySQLCol1 = ' + cast(MSSQLCol1 as varchar(max)) + ' where MySQLCol2 = ' + cast(MSSQLCol2 as varchar(max)) + ';' FROM MSSQLTable
That resulted in a bunch of update statements with the keys I needed like:
'update sgidb.Example set MySQLCol1 = 12 where MySQLCol2 = 45;
But when I tried to do the same in MySQL I got a bunch of syntax errors. The web told me MySQL don't need the + operator to concatenate strings in a sentence, but it didn't work, neither writing the concatenate function explicitly. Any ideas?
You can use the CONCAT function which is available in MySQL as well as in SQL, like this:
SELECT CONCAT('update sgidb.Example set MySQLCol1 = ' , MSSQLCol1 , ' where MySQLCol2 = ' , MSSQLCol2 , ';' )FROM MSSQLTable
Now in the above solution you need to take care of the blank space after or before or even after and before the statement.
For tackling the above situation what you can do is to use the function CONCAT_WS, which is available in MySQL as well as in SQL:
SELECT CONCAT_WS(' ', 'update sgidb.Example set MySQLCol1 =' , MSSQLCol1 , 'where MySQLCol2 =' , MSSQLCol2 , ';' )FROM MSSQLTable
CONCAT_WS function adds two or more strings together with a separator.
Now no need to take care of the spaces that you need to put to avoid the syntax error anymore.
Please note that, CONCAT_WS is going to handle null as well. But in case of CONCAT, if any of the variable/field is null then the entire CONCATENATED result becomes null.
Here's how you can do it
SELECT concat("update sgidb.Example set MySQLCol1 = ",MSSQLCol1,"where MySQLCol2 = ",MSSQLCol2,";") FROM MSSQLTable;
Both SQL Server and mysql have got CONCAT function.
You can use the below query in both RDBMS.
SELECT CONCAT('update sgidb.Example set MySQLCol1 = ' , MSSQLCol1 , ' where MySQLCol2 = ' , MSSQLCol2 , ';' )FROM MSSQLTable
You may try concat to concatenate your string.
Example as:
SELECT CONCAT('MySQL CAST example #',CAST(2 AS CHAR));
#ouput
MySQL CAST example #2
In your above update query your set and where column may have varchar, but if you closely look into your query ' is missing in both of the column names. Please try the below query.
SELECT CONCAT('update sgidb.Example set MySQLCol1 = ''',
CAST(MSSQLCol1 AS VARCHAR(MAX)),
''' where MySQLCol2 = ''',
CAST(MSSQLCol2 AS VARCHAR(MAX)) ,
''';' )
FROM MSSQLTable;

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.

Where clause not working in stored procedure, when working outside of it

We built a piece of dynamic sql that generates a wide view from data in long format. Seen here:
CREATE PROCEDURE `selectPivotedTermpoints`(studyid varchar(300))
BEGIN
SET SESSION group_concat_max_len = 10000000;
SET #psql = NULL;
SET #finalSQL = NULL;
SET #StudyID = studyid;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT('SUM(CASE WHEN terminate = ''', REPLACE(Terminate,'''', ''''''), ''' THEN 1 ELSE 0 END) AS `', REPLACE(Terminate,'''', ''), '`')
) INTO #psql
FROM Dashboard
WHERE studyid = #StudyID
AND completion_status = 'terminate';
SET #finalSQL = CONCAT('
SELECT Sample_provider as Provider,
completion_status as `Status`,',
#psql,'
FROM Dashboard
WHERE studyid = ''', #StudyID, '''
AND completion_status = ''terminate''
GROUP BY Sample_provider');
SELECT #finalSQL;
PREPARE stmt FROM #finalSQL;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END
When the sql is run as a query,(from SET to DEALLOCATE)setting #StudyID manually, we return a table with only the columns for that specific study(distinct Terminate as columns for only that study), however when the query is turned into a stored procedure and run it is generating a table with columns for all studies(all distinct Terminate as columns).
It appears that the first where clause (in the select group_concat) is being ignored when run as a stored procedure, but this is not the case when run as a simple query.
Stored procedure call:
selectPivotedTermpoints('bhp_03a');
Does anyone know why this is the case and / or how I can correct the issue?
I helped someone with a similar issue recently in another question; it confused us for quite a while. Change the parameter name to something else, I am guessing that WHERE is using it instead of the field in the table.
(You might be able to get away with Dashboard.studyid as well, but changing the parameter name will cause less confusion; and I am not positive how the query in #finalSQL would behave either.)

Dynamic MySQL Update Statement

I'm trying to write a prodecure that updates a value in a given column-name where the users id equals given user ID.
_strong_1 is a variable that contains the column name, i.e: 'category_1', for example.
SELECT COLUMN_NAME FROM information_schema.`COLUMNS` C
WHERE table_name = 'subscribers_preferences' AND COLUMN_NAME LIKE _strong_1 INTO #columns;
SET #table = 'subscribers_preferences';
SET #s = CONCAT('UPDATE ',#table,' SET ', #columns = 1);
PREPARE stmt FROM #s;
EXECUTE stmt;
There's an error within the 'SET #s =' statement. I can get it to work with a simple SELECT statement, but UPDATE is being tricky.
Thanks in advance.
You need to put = 1 in quotes.
SET #s = CONCAT('UPDATE ',#table,' SET ', #columns, ' = 1');
Otherwise, you're comparing #columns with 1, and concatenating either 1 or 0 (probably always 0, since I doubt you have a column named 1) to the SQL, which is creating invalid SQL.
Note that the above code will only update one column. If #columns is supposed to hold 3 columns, you need to use GROUP_CONCAT in your query that sets it.
SELECT GROUP_CONCAT(CONCAT(column_name, ' = 1')) AS #columns
FROM information_schema.columns
WHERE table_name = 'subscribers_preferences' and column_name LIKE _strong_1;
SET #table = 'subscribers_preferences';
SET #s = CONCAT('UPDATE ',#table,' SET ', #columns);
I suspect you also need to add a WHERE clause to this SQL so it just updates the row for the given ID. As currently written, it will update all rows.
The fact that you need to write the query like this suggests improper normallization of your data. Instead of having each preference option in a different column, they should be different rows of the table, with the key being something like (user_id, setting_name).