Convert column name to time - sql

There is a table TABLE which stores values for various intervals of time in a given day. The day can be divided in to 24 intervals of an hour each or 96 intervals of 15 mins duration each. TABLE contains the following columns
DATE
INTERVALCOUNT
INT001
INT002
.
.
INT096
I need to display the values in the table in the following format where based on the INTERVALCOUNT value the time is calculated and its associated value is displayed
SAMPLE OUTPUT
DATE TIME INTERVALCOUNT VALUE
2013-04-11 00:00:00.0000000 96 23.43
2013-04-11 00:15:00.0000000 96 26.91
2013-04-11 00:30:00.0000000 96 28.1999999999
2013-04-11 00:45:00.0000000 96 28.77
2013-04-23 00:00:00.0000000 24 18.3099999999
2013-04-23 01:00:00.0000000 24 20.94
CODE
SELECT [DATE],
(CASE
WHEN [INTERVALCOUNT]=96 THEN CAST(DATEADD(MINUTE,CAST(SUBSTRING([INTERVAL],4,3) AS INT)*15, [DATE]) AS Time)
WHEN [INTERVALCOUNT]=24 THEN CAST(DATEADD(HOUR,CAST(SUBSTRING([INTERVAL],4,3) AS INT), [DATE]) AS Time)
END) AS [TIME],
[INTERVALCOUNT], --24/96
[VALUE]
FROM ( SELECT [DATE],
[INTERVALCOUNT], --24/96
[INT001], [INT002], [INT003], [INT004], [INT005], [INT006], [INT007], [INT008],
[INT009], [INT010], [INT011], [INT012], [INT013], [INT014], [INT015], [INT016],
[INT017], [INT018], [INT019], [INT020], [INT021], [INT022], [INT023], [INT024],
[INT025], [INT026], [INT027], [INT028], [INT029], [INT030], [INT031], [INT032],
[INT033], [INT034], [INT035], [INT036], [INT037], [INT038], [INT039], [INT040],
[INT041], [INT042], [INT043], [INT044], [INT045], [INT046], [INT047], [INT048],
[INT049], [INT050], [INT051], [INT052], [INT053], [INT054], [INT055], [INT056],
[INT057], [INT058], [INT059], [INT060], [INT061], [INT062], [INT063], [INT064],
[INT065], [INT066], [INT067], [INT068], [INT069], [INT070], [INT071], [INT072],
[INT073], [INT074], [INT075], [INT076], [INT077], [INT078], [INT079], [INT080],
[INT081], [INT082], [INT083], [INT084], [INT085], [INT086], [INT087], [INT088],
[INT089], [INT090], [INT091], [INT092], [INT093], [INT094], [INT095], [INT096]
FROM [TABLE] ) [Source]
UNPIVOT ([VALUE] FOR [INTERVAL] IN
([INT001], [INT002], [INT003], [INT004], [INT005], [INT006], [INT007], [INT008],
[INT009], [INT010], [INT011], [INT012], [INT013], [INT014], [INT015], [INT016],
[INT017], [INT018], [INT019], [INT020], [INT021], [INT022], [INT023], [INT024],
[INT025], [INT026], [INT027], [INT028], [INT029], [INT030], [INT031], [INT032],
[INT033], [INT034], [INT035], [INT036], [INT037], [INT038], [INT039], [INT040],
[INT041], [INT042], [INT043], [INT044], [INT045], [INT046], [INT047], [INT048],
[INT049], [INT050], [INT051], [INT052], [INT053], [INT054], [INT055], [INT056],
[INT057], [INT058], [INT059], [INT060], [INT061], [INT062], [INT063], [INT064],
[INT065], [INT066], [INT067], [INT068], [INT069], [INT070], [INT071], [INT072],
[INT073], [INT074], [INT075], [INT076], [INT077], [INT078], [INT079], [INT080],
[INT081], [INT082], [INT083], [INT084], [INT085], [INT086], [INT087], [INT088],
[INT089], [INT090], [INT091], [INT092], [INT093], [INT094], [INT095], [INT096]) ) [Unpivot]
I have achieved it by using UNPIVOT and for displaying the time is use CAST along with SUBSTRING. Is there a better way of doing this? especially the part where I convert the intervals to time.
EDIT The table design can't be changed

Is there a better way of doing this?
Depends on what you mean by better. For instance, the following expression for TIME calculation is "better" in terms of the amount of code:
DATEADD(
MINUTE,
CAST(SUBSTRING([INTERVAL],4,3) AS INT) * 1440 / INTERVALCOUNT,
CAST('00:00' AS time)
) AS [TIME]
You might also find it more scalable (in fact, actually scalable): if you needed to add support for, say, half-hour intervals, you would only need to start using INTERVALCOUNT = 48 with your data.
At the same time, the intent may be conveyed in a less clear way with the above code. Therefore, if 24 and 96 are the only possible values you can ever need for INTERVALCOUNT, you will probably not need any more flexibility than what you've got already for that part of your code.

You haven't mentioned what the purpose of the output is but I assume it's some sort of report?
I don't think there is a better way of doing this. What you have there is a classic use case for UNPIVOT and your solution seems appropriate to me, given that you can't change the underlying data structure.

Try this one -
DECLARE
#cols NVARCHAR(MAX)
, #SQL NVARCHAR(MAX)
SELECT #cols = STUFF((
SELECT ', [' + c.name + ']'
FROM sys.columns c
WHERE c.name LIKE 'INT%'
AND c.[object_id] = OBJECT_ID('dbo.TABLE')
FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)'), 1, 2, '')
SELECT #SQL = '
SELECT
[DATE]
, [TIME] = CASE
WHEN [INTERVALCOUNT] = 96
THEN CAST(DATEADD(MINUTE, CAST(SUBSTRING([INTERVAL],4,3) AS INT)*15, [DATE]) AS Time)
WHEN [INTERVALCOUNT] = 24
THEN CAST(DATEADD(HOUR, CAST(SUBSTRING([INTERVAL],4,3) AS INT), [DATE]) AS Time)
END
, [INTERVALCOUNT]
, [VALUE]
FROM (
SELECT [DATE], [INTERVALCOUNT], ' + #cols + '
FROM [TABLE]
) s
UNPIVOT ([VALUE] FOR [INTERVAL] IN (' + #cols + ') ) up
'
PRINT #SQL
EXEC sys.sp_executesql #SQL

Related

How can I check the maximum value from a set of tables in SQL Server (if possible)?

We have a set of databases (80 in total). Every single one has a table called tblProfessions. The tables are not standardized. For example:
EDIT: all the databases are on the same server.
The DB1.dbo.tblProfessions is like:
intProfessionCode
strProfessionDescription
1
lawyer
2
dentist
...
...
30
doctor
And the DB72.dbo.tblProfessions is as follows:
intProfessionCode
strProfessionDescription
1
designer
2
butcher
...
...
80
chef
Suppose I ran a script from DBO1 to DBO72, and I found that the biggest table has 80 entries (in this case the DBO72 is the biggest one).
By my limited knowledge, all I know is to run the below script database by database, and write it down in a spreadsheet manually:
SELECT MAX(intProfessionCode) FROM [DB].dbo.tblProfessions;
Is there a script to run and loop through all the tblProfessions and get the one with the most entries? All I want is the biggest number found.
Thanks in advance.
You should be able to do something like this:
WITH dat
AS
(
SELECT 'db1' AS database_name, MAX(intProfessionCode) AS max_intProfessionCode
FROM DB1.dbo.tblProfessions
UNION ALL
...
UNION ALL
SELECT 'db72' AS database_name, MAX(intProfessionCode) AS max_intProfessionCode
FROM DB72.dbo.tblProfessions
)
SELECT dat.db, dat.max_intProfessionCode
FROM dat
INNER JOIN (SELECT MAX(max_intProfessionCode) AS max_intProfessionCode_overall
FROM dat) y
ON dat.max_intProfessionCode = y.max_intProfessionCode_overall
For situations like this, I usually query the catalog to write the above script rather than typing it out:
WITH
dat AS
(
SELECT STRING_AGG('SELECT ''' + QUOTENAME(s.name) + ''' AS db,
MAX(intProfessionCode) AS max_intProfessionCode
FROM ' + QUOTENAME(s.name) + '.' + QUOTENAME('dbo') + '.' + QUOTENAME('tblProfessions') + '
UNION ALL',' ') AS s
FROM sys.databases s
WHERE s.name LIKE 'DB%' --EDIT APPROPRIATELY
)
SELECT 'WITH dat
AS
(' + SUBSTRING(s,1,LEN(s) - LEN(' UNION ALL')) + ')
SELECT dat.db, dat.max_intProfessionCode
FROM dat
INNER JOIN (SELECT MAX(max_intProfessionCode) AS max_intProfessionCode_overall
FROM dat) y
ON dat.max_intProfessionCode = y.max_intProfessionCode_overall' AS scrpt
FROM dat;
Make sure the above is only returning data for the appropriate databases by editing the WHERE clause in the CTE, then copy the output, paste elsewhere and run it to get your results.

Get Values between Each Comma in Seperate Row in SQL Server

I need to insert multiple rows in a database table from a single string.
Here is my string it will be comma-seperated values
Current string:
batch 1 45665 987655,1228857 76554738,12390 8885858,301297 38998798
What I want is that batch 1 should be ignored or removed and remaining part should be added into the SQL Server database as a separate row for after each comma like this
Table name dbo.MSISDNData
Data
------------------
45665 987655
1228857 76554738
12390 8885858
301297 38998798
and when I query the table it should return the results like this
Query :
Select data
from dbo.MSISDNData
Results
Data
---------------------
45665 987655
1228857 76554738
12390 8885858
301297 38998798
Try this:
DECLARE #Data NVARCHAR(MAX) = N'batch 1 45665 987655,1228857 76554738,12390 8885858,301297 38998798'
DECLARE #DataXML XML;
SET #Data = '<a>' + REPLACE(REPLACE(#Data, 'batch 1 ', ''), ',', '</a><a>') + '</a>';
SET #DataXML = #Data;
SELECT LTRIM(RTRIM(T.c.value('.', 'VARCHAR(MAX)'))) AS [Data]
FROM #DataXML.nodes('./a') T(c);
It demonstrates how to split the data. You may need to sanitize it, too - remove the batch 1, perform trimming, etc.

Issue with result set from stored procedure report generation

I have a Visual studio based stored procedure that generates a report for a monthly audit process. In the database being queried, all data for each month lives in its own individual table (Contacts_month_1, Contacts_month_2, etc.)
The SQL used in this report generation has some minor logic included, to allow it to work dynamically, rather than use hard coded dates. The problem arose at the start of January 2017, when I started receiving not just the results for the prior month, but additionally the prior year as well. To be specific, the audit report for December 2016 included data for both 12/2016 and 12/2015. Initially I thought it was a fluke of some kind based on the turn of the year, and we have not had this automated process during the turn as of yet. Unfortunately when I came in to the office today, inside the output file for January 2017, I also received the results for January 2016.
I attempted to include a year check to the process, however I am still getting the same result output. Any ideas would be appreciated.
Declare #GetMonth TinyInt
,#SessionTable varchar(50)
,#ContactTable varchar(50)
,#TableVersion varchar(2)
Declare #GetYear SmallInt
,#SessionTable_year varchar(50)
,#ContactTable_year varchar(50)
,#TableVersion_year varchar(4)
Set #GetMonth=MONTH(cast(GetDate() as Datetime))-1
Set #GetYear=YEAR(cast(GetDate() as Datetime))
If (#getmonth=0) Set #GetMonth=12 + (#GetYear-1)
Set #TableVersion=CAST(#getMonth as varchar(2))
Set #SessionTable='[CentralDWH].[dbo].[Sessions_month_' +#tableversion +']'
Set #ContactTable ='[CentralDWH].[dbo].[Contacts_month_' +#tableversion +']'
-- Select #GetMonth,#GetYear (DEBUGGING STATEMENT)
-- Select #SessionTable,#ContactTable (DEBUGGING STATEMENT)
Exec('SELECT [PBX_id] as AgentID
,[p22_value] as Skill
,''Athens'' as Location
,Convert(varchar(20),c.[local_start_time],120) as local_start_time
,convert(varchar(20),c.[local_end_time],120) as local_end_time
,U.[USER_NAME]
,call_id
FROM '+#SessionTable +' S
Inner join dbo.Users U on S.user_key=U.user_key
inner Join '+ #ContactTable+' C on S.contact_key=C.contact_key
Where is_screen > 0
And Unit_Num between 398003 and 398005
and P22_value is not null
and c.[local_start_time] > ' + #GetYear
+ ' order by local_start_time')
As I understand, the #GetMonth variable is used for returning the previous month
Set #GetMonth = MONTH(CAST(GetDate() AS Datetime)) - 1
After a quick look after you procedure my first issue was this line of code:
IF (#getmonth = 0)
SET #GetMonth = 12 + (#GetYear - 1)
I don't understand why are you setting the #GetMonth variable to 12 + current year -1 and I assume this is the cause to the problem.
Did you want to get the 12th month of the previous year when the current month is 1 (January)? If yes then you can easily change the If block to
If #GetMonth = 0
Begin
Set #GetMonth = 12
Set #GetYear = #GetYear - 1
End
Other issues:
It's recommended to keep the consistency of the names of the variables #GetMonth, #getmonth, this will cause an error if the database collation is case sensitive.
#GetMonth is declared as TinyInt and this will cause an arithmetic overflow if you try to store the year
I recommend testing the dynamic SQL statement that you are composing here with some hard coded values to check the results returned, you can use January and 2016 to check if the actual issue in your procedure or it's in your query.
Hope it helps
Thanks for your help, I figured out the root of the problem, and it was because i was not casting the GetYear as a varchar when trying to run the T-SQL statement. This in turn caused the variable to be completely ignored. I also cleaned up the query a little bit after realizing i was goofing up pretty hard.
Below is the cleaned up functional query, so that it may help someone in the future:
Declare #GetMonth SmallInt,
#SessionTable varchar(50),
#ContactTable varchar(50),
#TableVersion varchar(2),
#GetYear SmallInt,
#YearCheck varchar(4)
Set #GetMonth=MONTH(cast(GetDate() as Datetime))-1
Set #GetYear=YEAR(cast(GetDate() as Datetime))-1
If (#GetMonth=0)
Begin
Set #GetMonth =12
Set #GetYear =#GetYear - 1
End
Set #TableVersion=CAST(#GetMonth as varchar(2))
Set #SessionTable='[CentralDWH].[dbo].[Sessions_month_' +#tableversion +']'
Set #ContactTable ='[CentralDWH].[dbo].[Contacts_month_' +#tableversion +']'
Set #YearCheck=CAST(#GetYear as varchar(4))
--Select #GetMonth,#GetYear,#YearCheck (DEBUGGING STATEMENT)
-- Select #SessionTable,#ContactTable (DEBUGGING STATEMENT)
Exec('SELECT
[PBX_id] as AgentID,
[p22_value] as Skill,
''Athens'' as Location,
Convert(varchar(20),c.[local_start_time],120) as local_start_time,
convert(varchar(20),c.[local_end_time],120) as local_end_time,
U.[USER_NAME],
call_id
FROM '+#SessionTable +' S
Inner join dbo.Users U on S.user_key=U.user_key
inner Join '+ #ContactTable+' C on S.contact_key=C.contact_key
Where is_screen>0
And Unit_Num between 398003 and 398005
And P22_value is not null
And year(c.[local_start_time]) > '+#YearCheck+'
order by local_start_time')
Once I cleaned all this up and remembered to cast the year properly, everything fell into place.

Creating Dynamic Dates as Variable (Column Names) in SQL

First, I have read about similar posts and have read the comments that this isn't an ideal solution and I get it but the boss (ie client) wants it this way. The parameters are as follows (for various reasons too bizarre to go into but trust me):
1. SQL Server Mgmt Studio 2016
2. NO parameters or pass throughs or temp tables. All has to be within contained code.
So here we go:
I need to create column headings that reflect dates:
1. Current date
2. Most recent quarter end prior to current date
3. Most recent quarter end prior to #2
4. Most recent quarter end prior to #3
5. Most recent quarter end prior to #4
6. Most recent quarter end prior to #5
So if using today's date, my column names would be as follows
12/18/2016 9/30/2016 6/30/2016 3/31/2016 12/31/2016 9/30/2015
I can easily do it in SAS but can't in SQL given the requirements stated above.
Help please with same code.
Thank you
Paula
Seems like a long way to go for something which really belongs in the presentation layer. That said, consider the following:
Let's assume you maintain a naming convention for your calculated fields, for example [CurrentDay], [QtrMinus1], [QtrMinus2], [QtrMinus3], [QtrMinus4],[QtrMinus5]. Then we can wrap your complicated query in some dynamic SQL.
Just as an illustration, let's assume your current query results looks like this
After the "wrap", the results will then look like so:
The code - Since you did NOT exclude Dynamic SQL.
Declare #S varchar(max)='
Select [CustName]
,['+convert(varchar(10),GetDate(),101)+'] = [CurrentDay]
,['+Convert(varchar(10),EOMonth(DateFromParts(Year(DateAdd(QQ,-1,GetDate())),DatePart(QQ,DateAdd(QQ,-1,GetDate()))*3,1)),101)+'] = [QtrMinus1]
,['+Convert(varchar(10),EOMonth(DateFromParts(Year(DateAdd(QQ,-2,GetDate())),DatePart(QQ,DateAdd(QQ,-2,GetDate()))*3,1)),101)+'] = [QtrMinus2]
,['+Convert(varchar(10),EOMonth(DateFromParts(Year(DateAdd(QQ,-3,GetDate())),DatePart(QQ,DateAdd(QQ,-3,GetDate()))*3,1)),101)+'] = [QtrMinus3]
,['+Convert(varchar(10),EOMonth(DateFromParts(Year(DateAdd(QQ,-4,GetDate())),DatePart(QQ,DateAdd(QQ,-4,GetDate()))*3,1)),101)+'] = [QtrMinus4]
,['+Convert(varchar(10),EOMonth(DateFromParts(Year(DateAdd(QQ,-5,GetDate())),DatePart(QQ,DateAdd(QQ,-5,GetDate()))*3,1)),101)+'] = [QtrMinus5]
From (
-- Your Complicated Query --
Select * from YourTable
) A
'
Exec(#S)
If it helps the visualization, the generated SQL is as follows:
Select [CustName]
,[12/18/2016] = [CurrentDay]
,[09/30/2016] = [QtrMinus1]
,[06/30/2016] = [QtrMinus2]
,[03/31/2016] = [QtrMinus3]
,[12/31/2015] = [QtrMinus4]
,[09/30/2015] = [QtrMinus5]
From (
-- Your Complicated Query --
Select * from YourTable
) A
Here is one way using dynamic query
DECLARE #prior_quarters INT = 4,
#int INT =1,
#col_list VARCHAR(max)=Quotename(CONVERT(VARCHAR(20), Getdate(), 101))
WHILE #int <= #prior_quarters
BEGIN
SELECT #col_list += Concat(',', Quotename(CONVERT(VARCHAR(20), Eomonth(Getdate(), ( ( ( ( Month(Getdate()) - 1 ) % 3 ) + 1 ) * -1 ) * #int), 101)))
SET #int+=1
END
--SELECT #col_list -- for debugging
EXEC ('select '+#col_list+' from yourtable')

SQL fails to convert types, but why

I am trying to filter a query the following way:
declare #CubeYear as varchar(30)
--Setting it this way so it can later be easily used in SSAS Cubes
set #CubeYear = '[Date].[Year].&[2013]'
SELECT [RankingID]
,[Year]
,[Customer]
,[Rank]
FROM [OBase].[dbo].[fact_KundeRanking]
where '[Date].[Year].&[' + Year + ']' = #CubeYear
but I keep getting the following error:
Conversion failed when converting the varchar value '[Date].[Year].&[2013]' to data type int.
Does anybody know what the solution to this might be?
Try this -
where '[Date].[Year].&[' + CAST(Year as varchar(4)) + ']' = #CubeYear