Convert dynamic SQL Server Pivot to MySQL - mysql

I have written below stored procedure in sql server
CREATE PROCEDURE usp_getQuantityBilling()
-- VARIABLE --
DECLARE #cols AS NVARCHAR(MAX)
DECLARE #query AS NVARCHAR(MAX)
select #cols = group_concat(name)
from visit_groups where arm_id='377';
set #query = 'SELECT id, service, rate, subjectcount, armDetailsId, totalHours,'
+ #cols + ' FROM
(
select protocols.id, arms.name, visit_groups.name as visitGroup, services.name as serviceName, visits.quantity,
line_items_visits.subject_count
from protocols,arms,visit_groups,visits,line_items,line_items_visits,services
where protocols.id = arms.protocol_id
and visit_groups.arm_id = arms.id
and visits.visit_group_id = visit_groups.id
and line_items_visits.arm_id = arms.id
and visits.line_items_visit_id = line_items_visits.id
and line_items_visits.line_item_id = line_items.id
and services.id = line_items.service_id
and protocols.id = ''' + 591 + '''
) x
pivot
(
max(isActive)
for visitName in (' + #cols + ')
) p';
execute (#query)
I am trying to convert this into mysql server but I am not sure how to declare the variables in mysql stored proc and how to run all these quesry together where result generated by previous query can be used in next query.
Also below query:
select #cols = group_concat(name)
from visit_groups where arm_id='377';
is giving error
Error Code: 1267. Illegal mix of collations (utf8_general_ci,IMPLICIT)
and (utf8_unicode_ci,IMPLICIT) for operation '=' 0.000 sec
so i looked for the solution and made it like this
select #cols = group_concat(name) COLLATE utf8_unicode_ci from visit_groups
where arm_id='377';
Now I am getting result as 0 with a column name #cols = group_concat(name) COLLATE utf8_unicode_ci , but the result should actually look like visit1, visit 2 because that is what I am getting in sql server.
Edit
SELECT id, name, visitGroup, serviceName, quantity, subject_count,'
+ [Visit 1],[Visit 2] + ' FROM
(
select protocols.id, arms.name, visit_groups.name as visitGroup, services.name as serviceName,
visits.quantity,
line_items_visits.subject_count
from protocols,arms,visit_groups,visits,line_items,line_items_visits,services
where protocols.id = arms.protocol_id
and visit_groups.arm_id = arms.id
and visits.visit_group_id = visit_groups.id
and line_items_visits.arm_id = arms.id
and visits.line_items_visit_id = line_items_visits.id
and line_items_visits.line_item_id = line_items.id
and services.id = line_items.service_id
and protocols.id = ''' + 591 + '''
) x
pivot
(
max(isActive)
for visitName in (' + [Visit 1],[Visit 2] + ')
) p'
main issue is running this query, it shows error in mysql that pivot is used at wrong position but in sql it works fine. Anyway to turn this query into mysql pivot query.
Note: this Visit 1 and Visit 2 I am generating dynamically but just for this solution I copied here manually.

Related

Omitting Scientific Notation in Numeric columns when working with Dynamic SQL returning JSON

The below dynamic query returns the output of the numeric columns in Scientific notation.
DECLARE #Cols VARCHAR(MAX) = (SELECT STUFF((
SELECT ',' +'['+ Description + ']' FROM #PermittedColumnIDs
DECLARE #Query NVARCHAR(MAX) = 'SELECT TOP 1000 '+ #Cols +' FROM ' + (SELECT ViewName FROM
#DynamicQueryProps) + ' FOR JSON AUTO';
EXECUTE sp_executesql #Query
JSON output
Tabular output
As above mentioned, tabular view returns the AWP value properly and JSON view returns it with the scientific notation. How to get the JSON AWP column without Sc. notation.
Please note that
It needs to preserve the numeric value as numeric without converting it to a string in the JSON result.
Cannot change the format of individual columns since columns are dynamic.
It's too long for a comment, so I post this as an answer. I'm able to find only these explanations in the documentation about how FOR JSON converts SQL Server data types to JSON types. So, as a possible workaround, you may try to convert the float columns to numeric using information from system catalog views (I assume, that the SELECT is against a view).
DECLARE #cols varchar(MAX) = STUFF(
(
SELECT
', ' +
CASE
WHEN t.[name] = 'float' THEN 'CONVERT(numeric(10, 2), [' + p.[description] + ']) AS [' + p.[description] + N'] '
ELSE p.[description]
END
FROM sys.columns c
JOIN sys.views v ON c.object_id = v.object_id
JOIN sys.schemas s ON v.schema_id = s.schema_id
JOIN sys.types t ON c.system_type_id = t.system_type_id
JOIN #PermittedColumnIDs p ON p.[description] = c.[name]
WHERE v.[name] = (SELECT ViewName FROM #DynamicQueryProps) AND s.[name] = 'dbo'
FOR XML PATH(''), TYPE
).value('.', 'varchar(max)'), 1, 1, ''
)
DECLARE #query nvarchar(max)
SET #query =
N' SELECT TOP 1000 '+ #Cols +
N' FROM [' + (SELECT ViewName FROM #DynamicQueryProps) + ']' +
N' FOR JSON AUTO';
EXECUTE sp_executesql #query

Using CTE with a dynamic pivot

I'm trying to use This question to perform a dynamic pivot, but I want to use a CTE to get the initial data.
My query looks like this:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
WITH dataSet (coDate, TransactionDate, TotalBalance, TransDate, collected)
AS
( *SELECT STATEMENT )
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(c.category)
FROM dataSet c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT coDate, ' + #cols + ' from
(
select coDate
, TotalBalance
, collected
, TransDate
from dataSet
) x
pivot
(
SUM(collected)
for category in (' + #cols + ')
) p '
execute(#query)
And the error SQL gives me is Incorrect syntax near the keyword 'SET'. I did try adding a semicolon and go as well as a comma before the SET statement, but this the first time I've used PIVOT so I'm not sure how CTE interacts with it.

Find minimum and maximum column value of a common column across all tables in a SQL Server 2008 database

I am examining a third party SQL Server 2008 database. In this database, there are 2 columns CREATED_DATETIME and UPDATED_DATETIME, which are present in majority of the tables, but probably not all.
I want to find the minimum and maximum value of these 2 columns across all tables in the database which have these 2 columns. That will give me a fair idea that the data in the database is from which period to which period.
How can I write such a query?
Something like the following should work
DECLARE #C1 AS CURSOR,
#TABLE_SCHEMA SYSNAME,
#TABLE_NAME SYSNAME,
#HasCreated BIT,
#HasUpdated BIT,
#MaxDate DATETIME,
#MinDate DATETIME,
#SQL NVARCHAR(MAX)
SET #C1 = CURSOR FAST_FORWARD FOR
SELECT TABLE_SCHEMA,
TABLE_NAME,
COUNT(CASE
WHEN COLUMN_NAME = 'CREATED_DATETIME' THEN 1
END) AS HasCreated,
COUNT(CASE
WHEN COLUMN_NAME = 'UPDATED_DATETIME' THEN 1
END) AS HasUpdated
FROM INFORMATION_SCHEMA.COLUMNS
WHERE COLUMN_NAME IN ( 'CREATED_DATETIME', 'UPDATED_DATETIME' )
GROUP BY TABLE_SCHEMA,
TABLE_NAME
OPEN #C1;
FETCH NEXT FROM #C1 INTO #TABLE_SCHEMA , #TABLE_NAME , #HasCreated , #HasUpdated ;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #SQL = N'
SELECT #MaxDate = MAX(D),
#MinDate = MIN(D)
FROM ' + QUOTENAME(#TABLE_SCHEMA) + '.' + QUOTENAME(#TABLE_NAME) + N'
CROSS APPLY (VALUES ' +
CASE WHEN #HasCreated = 1 THEN N'(CREATED_DATETIME),' ELSE '' END +
CASE WHEN #HasUpdated = 1 THEN N'(UPDATED_DATETIME),' ELSE '' END + N'
(#MaxDate),
(#MinDate)) V(D)
'
EXEC sp_executesql
#SQL,
N'#MaxDate datetime OUTPUT, #MinDate datetime OUTPUT',
#MaxDate = #MaxDate OUTPUT,
#MinDate = #MinDate OUTPUT
FETCH NEXT FROM #C1 INTO #TABLE_SCHEMA , #TABLE_NAME , #HasCreated , #HasUpdated ;
END
SELECT #MaxDate AS [#MaxDate], #MinDate AS [#MinDate]
select MIN(CREATED_DATETIME) MinCREATED_DATETIME_Table1, MAX(CREATED_DATETIME) MaxCREATED_DATETIME_Table1, MIN(CREATED_DATETIME) MinCREATED_DATETIME_Table2, MAX(CREATED_DATETIME) MaxCREATED_DATETIME_Table2 from Table1, Table2
Run this script in SSMS (CTrl+T=text results, F5=execute query):
SET NOCOUNT ON;
SELECT 'SELECT MIN('
+ QUOTENAME(c.COLUMN_NAME)
+ ') AS '
+ QUOTENAME('Min '+c.TABLE_NAME+'.'+c.COLUMN_NAME)
+ ', MAX('
+ QUOTENAME(c.COLUMN_NAME)
+ ') AS '
+ QUOTENAME('Max_'+c.TABLE_NAME+'.'+c.COLUMN_NAME)
+ CHAR(13)
+ 'FROM ' + QUOTENAME(c.TABLE_SCHEMA)+'.'+QUOTENAME(c.TABLE_NAME)
FROM INFORMATION_SCHEMA.COLUMNS c
WHERE c.COLUMN_NAME IN ('CREATED_DATETIME', 'UPDATED_DATETIME')
ORDER BY c.TABLE_SCHEMA, c.TABLE_NAME, c.COLUMN_NAME;
SET NOCOUNT OFF;
It will generate another script. Execute generated script.
Example (generated script for master database and all tables with low column name > WHERE c.COLUMN_NAME IN (N'low')):
SELECT MIN([low]) AS [Min spt_fallback_dev.low], MAX([low]) AS [Max_spt_fallback_dev.low]
FROM [dbo].[spt_fallback_dev]
SELECT MIN([low]) AS [Min spt_values.low], MAX([low]) AS [Max_spt_values.low]
FROM [dbo].[spt_values]
Run the script mentioned in the link below. You will have to slightly alter as per your requirements.
SCRIPT to Search every Table and Field

Dropping all View under particular schema - SQL

How to drop all views under particular schema.
Ex: if I got below views in db.
[dbo].[view1]
[dbo].[veiw2]
[dbo].[view3]
[myView].[view1]
[myView].[veiw2]
[myView].[view3]
I just want to drop all views under schema myView all at a time.
Try this:
select 'drop view ' + QUOTENAME(sc.name) + '.' + QUOTENAME(obj.name) + ';'
from sys.objects obj
INNER JOIN sys.schemas sc
ON sc.schema_id = obj.schema_id
where obj.type='V'
and sc.name = 'myView';
I have tons of views (so, just aggregate drop query to one nvarchar(max) doesnt work - the query is truncated.) and i want to except some of views from deletion.
In this example, i want to drop every view from every schema beginnig with usr_* but not usr_test, usr_usr, usr_usr1, usr_usr2 and usr_usr3.
cursor is used because i dont care about few ms. This view cleaning query is used before integration tests, its not really speed-critical (and yet it perform really well)
declare drop_view_cursor cursor for
select 'drop view ' + QUOTENAME(sys.schemas.name) + '.' + QUOTENAME(sys.views.name) + ';'
from sys.views
inner join sys.schemas on sys.schemas.schema_id = sys.views.schema_id
where sys.schemas.schema_id in
(
select s.schema_id
from sys.schemas as s
where s.name like 'usr_%'
and s.name not in ('usr_test', 'usr_usr', 'usr_usr1', 'usr_usr2', 'usr_usr3')
)
declare #sql nvarchar(max)
open drop_view_cursor
fetch next from drop_view_cursor into #sql
while ##FETCH_STATUS = 0
begin
exec (#sql)
fetch next from drop_view_cursor into #sql
end
close drop_view_cursor
deallocate drop_view_cursor
This takes into account Schema, uses the system tables, and outputs to a variable that may then be executed. This way you don't have the problem of your Select truncating your script, because the Select and Print statements are limited on the length of what they may return.
The following runs in SQL Server 2008:
DECLARE #DropViewCommand nVarChar(MAX) = ''
SELECT #DropViewCommand = #DropViewCommand
+ 'DROP VIEW '+ QUOTENAME(S.Name) + '.' + QUOTENAME(V.name) + '; '
FROM sys.views as V
JOIN sys.schemas as S
ON S.schema_id = V.schema_id
WHERE S.name = 'dbo'--Selectively delete by Schema. Comment out to delete all views.
PRINT #DropViewCommand--See the command used in dropping all views (will be truncated in Select/Print, but not when Executing).
EXEC (#DropViewCommand)
Re-posting answer for those looking for specific solution. Put DROP view code in WHILE loop as shown below:
DECLARE #name VARCHAR(128)
DECLARE #SQL VARCHAR(254)
DECLARE #schema VARCHAR(128)
SELECT #name = (SELECT TOP 1 [name] FROM sysobjects WHERE [type] = 'V' AND category = 0 ORDER BY [name])
SELECT #schema = (SELECT TOP 1 schema_name(schema_id) FROM sys.views WHERE [name] = #name)
WHILE #name IS NOT NULL
BEGIN
SELECT #SQL = 'DROP VIEW [' + #schema + '].[' + RTRIM(#name) +']'
EXEC (#SQL)
PRINT 'Dropped View: ' + #name
SELECT #name = (SELECT TOP 1 [name] FROM sysobjects WHERE [type] = 'V' AND category = 0 AND [name] > #name ORDER BY [name])
SELECT #schema = (SELECT TOP 1 schema_name(schema_id) FROM sys.views WHERE [name] = #name)
END
GO

sql update with dynamic column names

EDIT: Database names have been modified for simplicity
I'm trying to get some dynamic sql in place to update static copies of some key production tables into another database (sql2008r2). The aim here is to allow consistent dissemination of data (from the 'static' database) for a certain period of time as our production databases are updated almost daily.
I am using a CURSOR to loop through a table that contains the objects that are to be copied into the 'static' database.
The prod tables don't change that frequently, but I'd like to make this somewhat "future proof" (if possible!) and extract the columns names from INFORMATION_SCHEMA.COLUMNS for each object (instead of using SELECT * FROM ...)
1) From what I have read in other posts, EXEC() seems limiting, so I believe that I'll need to use EXEC sp_executesql but I'm having a little trouble getting my head around it all.
2) As an added extra, if at all possible, i'd also like to exclude some columns for particular tables (structures vary slightly in the 'static' database)
here's what i have so far.
when executed, #colnames returns NULL and therefore #sql returns NULL...
could someone guide me to where i might find a solution?
any advice or help with this code is much appreciated.
CREATE PROCEDURE sp_UpdateRefTables
#debug bit = 0
AS
declare #proddbname varchar(50),
#schemaname varchar(50),
#objname varchar(150),
#wherecond varchar(150),
#colnames varchar(max),
#sql varchar(max),
#CRLF varchar(2)
set #wherecond = NULL;
set #CRLF = CHAR(10) + CHAR(13);
declare ObjectCursor cursor for
select databasename,schemaname,objectname
from Prod.dbo.ObjectsToUpdate
OPEN ObjectCursor ;
FETCH NEXT FROM ObjectCursor
INTO #proddbname,#schemaname,#objname ;
while ##FETCH_STATUS=0
begin
if #objname = 'TableXx'
set #wherecond = ' AND COLUMN_NAME != ''ExcludeCol1'''
if #objname = 'TableYy'
set #wherecond = ' AND COLUMN_NAME != ''ExcludeCol2'''
--extract column names for current object
select #colnames = coalesce(#colnames + ',', '') + QUOTENAME(column_name)
from Prod.INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME = + QUOTENAME(#objname,'') + isnull(#wherecond,'')
if #debug=1 PRINT '#colnames= ' + isnull(#colnames,'null')
--replace all data for #objname
--#proddbname is used as schema name in Static database
SELECT #sql = 'TRUNCATE TABLE ' + #proddbname + '.' + #objname + '; ' + #CRLF
SELECT #sql = #sql + 'INSERT INTO ' + #proddbname + '.' + #objname + ' ' + #CRLF
SELECT #sql = #sql + 'SELECT ' + #colnames + ' FROM ' + #proddbname + '.' + #schemaname + '.' + #objname + '; '
if #debug=1 PRINT '#sql= ' + isnull(#sql,'null')
EXEC sp_executesql #sql
FETCH NEXT FROM ObjectCursor
INTO #proddbname,#schemaname,#objname ;
end
CLOSE ObjectCursor ;
DEALLOCATE ObjectCursor ;
P.S. i have read about sql injection, but as this is an internal admin task, i'm guessing i'm safe here!? any advice on this is also appreciated.
many thanks in advance.
You have a mix of SQL and dynamic SQL in your query against information_schema. Also QUOTENAME isn't necessary in the where clause and will actually prevent a match at all, since SQL Server stores column_name, not [column_name], in the metadata. Finally, I'm going to change it to sys.columns since this is the way we should be deriving metadata in SQL Server. Try:
SELECT #colnames += ',' + name
FROM Prod.sys.columns
WHERE OBJECT_NAME([object_id]) = #objname
AND name <> CASE WHEN #objname = 'TableXx' THEN 'ExcludeCol1' ELSE '' END
AND name <> CASE WHEN #objname = 'TableYy' THEN 'ExcludeCol2' ELSE '' END;
SET #colnames = STUFF(#colnames, 1, 1, '');