How to pull day of month from Column header including row data? - sql

Trying to make a report where employee needs to work 30 working days since last point. I would like to have someway to show anything with 8.0 as working day. So column std_01_04 would show as 12/04/2020 and as a work day.
The sql database has been set up very goofy and having issues pulling from it.
there is a column named yr, data shows the current year "2020"
there is a column named mth_cal, show current month "12" for December.
The tricky part is the day. Column is std_01_15 the last 2 numbers are day of month, this example is the 15th. The data under that column will be a 0 or 8. 8 means they worked(full day) or 0 which means day off/holiday. So I need to pull data from a few spots to make a date and need to figure out 30 working days since last point. Any help would be great.
Edit: This was designed by different company. I have no way of redesigning this. I can only try to work with it. Or I need to go with manually entering all holidays/days off

You can create store procedure or function with input param #year and #month with code below to get all day with 8.0 working hours.
declare #year int = 2020
declare #month int = 11
create table #temp (workingDay date)
declare #index int = 1
declare #query nvarchar(200)
declare #paramdef nvarchar(300) = '#valOut nvarchar(10) OUTPUT'
declare #value nvarchar(10)
while #index <= 30
begin
set #query = 'select #valOut = STD_01_'+ REPLACE(STR(#index, 2), SPACE(1), '0') +' from work where yr='+ CAST(#year as nvarchar(4));
exec sp_executesql #query, #paramdef, #valOut = #value output
if #value = '8.0'
begin
insert into #temp values(CAST(#year as nvarchar(4)) + REPLACE(STR(#month + 1, 2), SPACE(1), '0') + REPLACE(STR(#index, 2), SPACE(1), '0'))
end
set #index = #index + 1
end
select * from #temp
drop table #temp
This is result when i run this code in SQL Server

I think you require results like below. You can use unpivot in sql server.
I have tested with below query.
Create table TestUnpivot(yr int , mnth int, std_01_01 int, std_01_02 int, std_01_03 int, primary key(yr,mnth) )
go
Insert into TestUnpivot values(2019,12,0,8,8),(2020,1,8,0,8),(2020,2,8,8,8)
go
Select * from TestUnpivot
go
Select Yr,Mnth, workd, dayss, Convert(Varchar(2),Mnth)+'/'+right(dayss,2)+'/'+convert(Varchar(4),Yr) as DT
from
(
select * from TestUnpivot
)p
unpivot
(
workd for dayss in (std_01_01,std_01_02,std_01_03)
) as unp
go

Related

Identity Column Format

I'm converting some data to SQL, and one column is a ID column in the format of last 2 digits of this year, and increment +1 of the last entered row in database.
So this year first entry would be 15-001, the next entry would be 15-002 etc. I cant seem to find an example anywhere online on how to do this? i'm not sure if I could do this in a Computed Column? Using Sql Server 2012.
Thank you!
I highly do not recommend using an ID in the format that you are stating. You will come across many other issues. You can use this code in a stored procedure or function to get next ID value.
DECLARE
#oldID varchar(8),
#newID varchar(8),
#temp varchar(8),
#number decimal,
#day int;
begin
SET #oldID = '15-555'; --select statement for last id
SET #day = YEAR(CURRENT_TIMESTAMP);
IF ( LEN(#oldID) = 6)
SET #temp = SUBSTRING(#oldID, 4, 6);
ELSE
SET #temp = SUBSTRING(#oldID, 3, 5);
SET #number = CAST(#temp AS Decimal(10, 0));
SET #number += 1;
SET #temp = CAST(#number AS varchar(8));
WHILE LEN(#temp) < 3
begin
SET #temp = '0' + #temp;
end
SET #newID = SUBSTRING(CAST(#day AS varchar(8)), 3, 4) + '-' + #temp;
print #newID --ID that you are looking for.
end
Stops working correctly after 15-999 and does not reset to 16-001 if that is what your were asking. Code can be changed to account for these things.

SQL operations on all columns of a table

I have many (>48) columns in one table, each column corresponds to a month and contains sales for that month. I need to create another table in which each column equals the addition of the previous 12 columns, e.g. getting the "rolling year" figure, so that e.g. July 2010 has everything from August 2009 through July 2010 added, August 2010 has everything from September 2009 through August 2010, and so on.
I could write this as:
select
[201007TOTAL] = [200908] + [200909] + ... + [201007]
,[201008TOTAL] = [200909] + ... + [201008]
...
...
into #newtable
from #mytable
I was wondering if there was a smarter way of doing this, either creating these as new columns in the table in one step, or perhaps pivoting the data, doing something to it, and re-pivoting?
Altough everybody is right, a different database set-up would be best, I thought this was a nice problem to play around with. Here's my setup:
CREATE TABLE TEST
(
ID INT
, [201401] decimal(19, 5)
, [201402] decimal(19, 5)
, [201403] decimal(19, 5)
, [201404] decimal(19, 5)
, [201405] decimal(19, 5)
, [201406] decimal(19, 5)
, [201407] decimal(19, 5)
)
INSERT INTO TEST
VALUES (1, 1, 2, 3, 4, 5, 6, 7)
Just one record with data is enough to test.
On the assumption the columns to be summed are consecutive in the table, and the first one is the first with datatype decimal. In other words, the table 'starts' (for want of better word) with a PK, which is usually INT, may be followed by descriptions or whatever, followed by the monthly columns to be summed:
DECLARE #OP_START INT
, #OP_END INT
, #LOOP INT
, #DATE VARCHAR(255)
, #SQL VARCHAR(MAX) = 'SELECT '
, #COLNAME VARCHAR(MAX)
-- Set Date to max date (=columnname)
SET #DATE = '201406'
-- Find Last attribute
SET #OP_END = (
SELECT MAX(ORDINAL_POSITION)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'TEST'
AND COLUMN_NAME <= #DATE
)
-- Find First attribute
SET #OP_START = (
SELECT MIN(ORDINAL_POSITION)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'TEST'
AND DATA_TYPE = 'DECIMAL'
)
SET #LOOP = #OP_START
-- Loop through the columns
WHILE #LOOP <= #OP_END
BEGIN
SET #COLNAME = (
SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'TEST'
AND ORDINAL_POSITION = #LOOP
)
-- Build SQL with found ColumnName
SET #SQL = #SQL + '[' + #COLNAME + ']' + '+'
SET #LOOP = #LOOP + 1
END
-- Remove last "+"
SET #SQL = SUBSTRING(#SQL, 1, LEN(#SQL) - 1)
-- Complete SQL
SET #SQL = #SQL + ' FROM TEST'
-- Execute
EXEC(#SQL)
This should keep adding up the monthly values, regardless how many you add. Just change the max date to what pleases you.
I'm NOT saying this is the best way to go, but it is a fun way :P

Date and Table name as parameter in Dynamic SQL

I'm Trying to create a stored procedure that will allow me to pick a start date and end date to get data from and to have a variable table name to write this data to.
I would like to pass in the two dates and the table name as parameters in the stored procedure. Here is that part I'm stuck on. I took out the stored procedure to try and get this working. this way I can see the lines the error is on.
DECLARE #MinDateWeek DATETIME
SELECT #MinDateWeek= DATEADD(WEEK, DATEDIFF(WEEK,0,GETDATE()), -7)
DECLARE #MaxDateWeek DATETIME
SELECT #MaxDateWeek= DATEADD(WEEK, DATEDIFF(WEEK,0,GETDATE()),0)
DECLARE #SQLCommand NVARCHAR(MAX)
SET #SQLCommand = ' --ERROR ON THIS LINE
-- Getting how much space is used in the present
DECLARE #Present Table (VMName NVARCHAR(50), UseSpace float(24))
INSERT INTO #Present
SELECT VMName
,SUM(CapacityGB-FreeSpaceGB)
FROM VMWareVMGuestDisk
GROUP BY VMName;
-- Getting how much space was used at the reference date
DECLARE #Past Table (VMName NVARCHAR(50), UseSpace float(24))
INSERT INTO #Past
SELECT VMName
,SUM(CapacityGB-FreeSpaceGB)
FROM VMWareVMGuestDisk
WHERE Cast([Date] AS VARCHAR(20))= '''+CAST(#MinDateWeek AS varchar(20))+'''
GROUP BY VMName;
--Inserting the average growth(GB/DAY) between the 2 dates in a Temporary Table
CREATE TABLE #TempWeek (VMName NVARCHAR(50)
, CapacityGB float(24)
, GrowthLastMonthGB float(24)
, FreeSpace FLOAT(24) )
INSERT INTO #TempWeek
SELECT DISTINCT V.VMName
,SUM(V.CapacityGB)
,SUM(((W1.UseSpace-W2.UseSpace)/(DATEDIFF(DAY,'''+CONVERT(VARCHAR(50),#MaxDateWeek)+''','''+CONVERT(VARCHAR (50),#MaxDateWeek)+'''))))
,SUM(V.FreeSpaceGb)
FROM VMWareVMGuestDisk AS V
LEFT JOIN
#Present AS W1
ON
V.VMName=W1.VMName
LEFT JOIN
#Past AS W2
ON
W1.VMName=W2.VMName
WHERE (CONVERT(VARCHAR(15),Date))='''+CONVERT(VARCHAR(50),#MaxDateWeek)+'''
GROUP BY V.VMName;
-- Checking if there is already data in the table
TRUNCATE TABLE SAN_Growth_Weekly;
--insert data in permanent table
INSERT INTO SAN_Growth_Weekly (VMName,Datacenter,Cluster,Company,DaysLeft,Growth, Capacity,FreeSpace,ReportDate)
SELECT DISTINCT
G.VMName
,V.Datacenter
,V.Cluster
,S.Company
, DaysLeft =
CASE
WHEN G.GrowthLastMonthGB IS NULL
THEN ''NO DATA''
WHEN (G.GrowthLastMonthGB)<=0
THEN ''UNKNOWN''
WHEN (G.FreeSpace/G.GrowthLastMonthGB)>0 AND (G.FreeSpace/G.GrowthLastMonthGB) <=30
THEN ''Less then 30 Days''
WHEN (G.FreeSpace/G.GrowthLastMonthGB)>30 AND (G.FreeSpace/G.GrowthLastMonthGB)<=60 THEN ''Less then 60 Days''
WHEN (G.FreeSpace/G.GrowthLastMonthGB)>60 AND (G.FreeSpace/G.GrowthLastMonthGB)<=90
THEN ''Less then 90 Days''
WHEN (G.FreeSpace/G.GrowthLastMonthGB)>90 AND (G.FreeSpace/G.GrowthLastMonthGB)<=180 THEN ''Less then 180 Days''
WHEN (G.FreeSpace/G.GrowthLastMonthGB)>180 AND (G.FreeSpace/G.GrowthLastMonthGB)<=365 THEN ''Less then 1 Year''
ELSE ''Over 1 Year''
END
,G.GrowthLastMonthGB
,G.CapacityGB
,G.FreeSpace
,'''+#MaxDateWeek+'''
FROM #tempWeek AS G
RIGHT JOIN VMWareVMGuestDisk AS V
ON V.VMName = G.VMName COLLATE SQL_Latin1_General_CP1_CI_AS
LEFT JOIN Server_Reference AS S
ON G.VMName COLLATE SQL_Latin1_General_CP1_CI_AS=S.[Asset Name]
WHERE '''+CONVERT(VARCHAR(50),#MaxDateWeek)+'''= CONVERT(VARCHAR(50),V.Date);'
EXEC sp_executesql #SQLCommand;
The error I get is
Conversion failed when converting date and/or time from character
string.
Thanks for the help.
Are you forgetting to enclose your Group By in the dynamic sql?:
ALTER PROCEDURE SAN_DISK_GROWTH
#MaxDateWeek DATETIME ,
#MinDateWeek DATETIME
AS
BEGIN
DECLARE #SQLCommand NVARCHAR(MAX)
SELECT #SQLCommand = '
DECLARE #Present Table (VMName NVARCHAR(50), UseSpace float(24))
INSERT INTO #Present
SELECT VMName
,SUM(CapacityGB - FreeSpaceGB)
FROM VMWareVMGuestDisk
WHERE CONVERT(VARCHAR(15),Date) = '''
+ CONVERT(VARCHAR(50), #MaxDateWeek) + ''' GROUP BY VMName;'
END
Try specifying your date/time values as parameters to the dynamic SQL query. In other words, instead of converting the dates to a varchar, use parameters in the query:
WHERE #MaxDateWeek = V.Date;
And pass the parameters on the call to sp_executesql like so:
EXEC sp_executesql #SQLCommand,
'#MindateWeek datetime, #MaxDateWeek datetime',
#MinDateWeek = #MinDateWeek,
#MaxDateWeek = #MaxDateWeek
Then you won't have to convert your dates to strings.
Note that this does not work for dynamic table names or column names. Those need to be concatenated together as part of the dynamic SQL itself.
For example, if you had a table name variable like this:
declare #TableName sysname
set #TableName = 'MyTable'
And you wanted the dynamic SQL to retrieve data from that table, then you would need to build your FROM statement like this:
set #SQLCommand = N'SELECT ...
FROM ' + #TableName + N' WHERE...
This build the name into the SQL like so:
'SELECT ... FROM MyTable WHERE...'

convert date to spanish in sql

I am running a report with multiple fields in english. I have 2 date fields at the end of the report, one has to be in English and the other in Spanish. The format the date needs to be in is ie November 1, 2012. I need the spanish to be Novembre 1, 2012. I cannot get the last field of the report to produce in spanish. I am running in sql server 2005.
Maybe is cumbersome, but I don't see how to do it on an easier way.
First, create a function. This function will make use of system view sys.syslanguages to get the correct name of month in spanish. Parameters are a valid date and language (alias on sys.syslanguage view).
CREATE FUNCTION [dbo].[fn_GetMonthName] (
#Date DATETIME,
#Language NVARCHAR(100)
)
RETURNS NVARCHAR(400)
AS
BEGIN
DECLARE #i INT, #m INT,#mlist NVARCHAR(1000)
SET #m = MONTH(#Date)
SET #mlist = (SELECT months FROM sys.syslanguages WHERE ALIAS = #language)
SET #i = 1
WHILE(#i < #m)
BEGIN
SET #mlist = REPLACE(#mlist, SUBSTRING(#mlist,1,CHARINDEX(',',#mlist)) ,'')
SET #i = #i + 1
END
SET #mlist = (CASE CHARINDEX(',',#mlist) WHEN 0 THEN #mlist ELSE SUBSTRING(#mlist,0,CHARINDEX(',',#mlist) ) END )
RETURN #mlist
END
GO
Then call the function anywhere you need to:
SELECT CONVERT(VARCHAR(20), GETDATE(), 100) AS CurrentDate,
dbo.fn_GetMonthName (GETDATE(), 'Spanish') AS [Mes-Month]
Result:
CurrentDate Mes-Month
May 24 2013 12:02AM Mayo
Taken from Get Language specific Month Name from SQL
Take a look at: http://www.sqlservercurry.com/2010/11/change-default-language-for-sql-server.html
You can temporarily set the language to spanish, not sure how feasible this is for you. The other way to do it would be to write your own months function, and maybe pass it a 2nd parameter that then decides what the output would be.
This function will translate the month within a string it's based on the sys.syslanguages table.
i.e. SELECT dbo.fn_tranMonth(2,0,'1 déc. 2014 10:26:14 UTC+00:00')
Results:
1 Dec. 2014 10:26:14 UTC+00:00
CREATE FUNCTION [dbo].[Split] (#sep char(1), #s varchar(8000))
RETURNS table
AS
RETURN (
WITH Pieces(pn, start, stop) AS (
SELECT 1, 1, CHARINDEX(#sep, #s)
UNION ALL
SELECT pn + 1, stop + 1, CHARINDEX(#sep, #s, stop + 1)
FROM Pieces
WHERE stop > 0
)
SELECT
SUBSTRING(#s, start, CASE WHEN stop > 0 THEN stop-start ELSE 512 END) AS s
FROM Pieces
)
GO
CREATE FUNCTION dbo.fn_tranMonth
(
#fromLan INT
,#toLan INT
,#string VARCHAR(MAX)
)
RETURNS
VARCHAR(50)
AS
BEGIN
DECLARE #TTTT AS TABLE(PK INT IDENTITY(1,1)
,fromMonth VARCHAR(50)
,toMonth VARCHAR(50)
)
DECLARE
#fromMonths VARCHAR(200)
,#toMonths VARCHAR(200)
,#fromMonth VARCHAR(20)
,#toMonth VARCHAR(20)
,#rowNum INT=12;
SELECT #fromMonths=shortmonths
FROM SYS.syslanguages
WHERE langid=#fromLan;
SELECT #toMonths=shortmonths
FROM sys.syslanguages
WHERE langid=#toLan;
INSERT #TTTT(fromMonth)
SELECT S
FROM dbo.Split(',',#fromMonths);
DECLARE #TTTT2 AS TABLE(PK INT IDENTITY(1,1)
,toMonth VARCHAR(50)
)
INSERT #TTTT2(toMonth)
SELECT S
FROM dbo.Split(',',#toMonths);
UPDATE #TTTT
SET toMonth=B.toMonth
FROM
#TTTT A
JOIN #TTTT2 B ON A.PK=B.PK;
DECLARE
#loopPos INT=0
,#returnMonth VARCHAR(50);
WHILE #loopPos<#rowNum
BEGIN
SET #loopPos+=1;
SELECT
#fromMonth=fromMonth
,#toMonth=toMonth
FROM #TTTT
WHERE PK=#loopPos;
SET #string=REPLACE(#string,#fromMonth,#toMonth);
END;
RETURN #string;
END
try this:
SELECT CONVERT(VARCHAR(10),GETDATE(), 103)
or
this code, return a VARCHAR(10) with date EN ESPAÑOL, leches.
IDEA (separator used: '-'):
Get format YYYY-MM-DD NVARCHAR(10).
Get format DD-MM-YYYY nvarchar(10)
Use the function
sample:
select dbo.date2txt(GETDATE ())
function to create:
create function [dbo].[AFecha] (
#fecha NVARCHAR(10)
)
returns NVARCHAR(10)
as
begin
Declare #r nvarchar(10)
if substring(#Fecha,3,1) = '-'
set #r = #Fecha
else
set #r = substring(#fecha,9,2)+'-'+substring(#fecha,6,2)+'-'+left(#fecha,4)
Return #r
End
go
create FUNCTION [dbo].[NTSDate](#fecha DateTime)
RETURNS nVarChar(10) AS
BEGIN
DECLARE #salida nVarChar(10)
set #salida = STR(year(#fecha),4,0) + '-' + RIGHT('0' + LTRIM(month(#fecha)),2) + '-' + RIGHT('0' + LTRIM(day(#fecha)),2)
return (#salida)
End
go
ALTER function [dbo].[Date2Txt](#Fecha as datetime) returns nvarchar(10)
as
begin
return dbo.afecha(dbo.NTSDate(#Fecha))
end
go
You can do the following:
FORMAT(GETDATE(), 'MMMM d,yyyy','es-US')
The last parameter for format datetime is an optional culture parameter that does exactly what you need when passed the right culture.
If you needed to localize to a different language you could find the string to do so from the link below.
https://learn.microsoft.com/en-us/windows-hardware/manufacture/desktop/default-input-locales-for-windows-language-packs?view=windows-11

SQL Server FOR EACH Loop

I have the following SQL query:
DECLARE #MyVar datetime = '1/1/2010'
SELECT #MyVar
This naturally returns '1/1/2010'.
What I want to do is have a list of dates, say:
1/1/2010
2/1/2010
3/1/2010
4/1/2010
5/1/2010
Then i want to FOR EACH through the numbers and run the SQL Query.
Something like (pseudocode):
List = 1/1/2010,2/1/2010,3/1/2010,4/1/2010,5/1/2010
For each x in List
do
DECLARE #MyVar datetime = x
SELECT #MyVar
So this would return:-
1/1/2010
2/1/2010
3/1/2010
4/1/2010
5/1/2010
I want this to return the data as one resultset, not multiple resultsets, so I may need to use some kind of union at the end of the query, so each iteration of the loop unions onto the next.
edit
I have a large query that accepts a 'to date' parameter, I need to run it 24 times, each time with a specific to date which I need to be able to supply (these dates are going to be dynamic) I want to avoid repeating my query 24 times with union alls joining them as if I need to come back and add additional columns it would be very time consuming.
SQL is primarily a set-orientated language - it's generally a bad idea to use a loop in it.
In this case, a similar result could be achieved using a recursive CTE:
with cte as
(select 1 i union all
select i+1 i from cte where i < 5)
select dateadd(d, i-1, '2010-01-01') from cte
Here is an option with a table variable:
DECLARE #MyVar TABLE(Val DATETIME)
DECLARE #I INT, #StartDate DATETIME
SET #I = 1
SET #StartDate = '20100101'
WHILE #I <= 5
BEGIN
INSERT INTO #MyVar(Val)
VALUES(#StartDate)
SET #StartDate = DATEADD(DAY,1,#StartDate)
SET #I = #I + 1
END
SELECT *
FROM #MyVar
You can do the same with a temp table:
CREATE TABLE #MyVar(Val DATETIME)
DECLARE #I INT, #StartDate DATETIME
SET #I = 1
SET #StartDate = '20100101'
WHILE #I <= 5
BEGIN
INSERT INTO #MyVar(Val)
VALUES(#StartDate)
SET #StartDate = DATEADD(DAY,1,#StartDate)
SET #I = #I + 1
END
SELECT *
FROM #MyVar
You should tell us what is your main goal, as was said by #JohnFx, this could probably be done another (more efficient) way.
You could use a variable table, like this:
declare #num int
set #num = 1
declare #results table ( val int )
while (#num < 6)
begin
insert into #results ( val ) values ( #num )
set #num = #num + 1
end
select val from #results
This kind of depends on what you want to do with the results. If you're just after the numbers, a set-based option would be a numbers table - which comes in handy for all sorts of things.
For MSSQL 2005+, you can use a recursive CTE to generate a numbers table inline:
;WITH Numbers (N) AS (
SELECT 1 UNION ALL
SELECT 1 + N FROM Numbers WHERE N < 500
)
SELECT N FROM Numbers
OPTION (MAXRECURSION 500)
declare #counter as int
set #counter = 0
declare #date as varchar(50)
set #date = cast(1+#counter as varchar)+'/01/2013'
while(#counter < 12)
begin
select cast(1+#counter as varchar)+'/01/2013' as date
set #counter = #counter + 1
end
Off course an old question. But I have a simple solution where no need of Looping, CTE, Table variables etc.
DECLARE #MyVar datetime = '1/1/2010'
SELECT #MyVar
SELECT DATEADD (DD,NUMBER,#MyVar)
FROM master.dbo.spt_values
WHERE TYPE='P' AND NUMBER BETWEEN 0 AND 4
ORDER BY NUMBER
Note : spt_values is a Mircrosoft's undocumented table. It has numbers for every type. Its not suggestible to use as it can be removed in any new versions of sql server without prior information, since it is undocumented. But we can use it as quick workaround in some scenario's like above.
[CREATE PROCEDURE [rat].[GetYear]
AS
BEGIN
-- variable for storing start date
Declare #StartYear as int
-- Variable for the End date
Declare #EndYear as int
-- Setting the value in strat Date
select #StartYear = Value from rat.Configuration where Name = 'REPORT_START_YEAR';
-- Setting the End date
select #EndYear = Value from rat.Configuration where Name = 'REPORT_END_YEAR';
-- Creating Tem table
with [Years] as
(
--Selecting the Year
select #StartYear [Year]
--doing Union
union all
-- doing the loop in Years table
select Year+1 Year from [Years] where Year < #EndYear
)
--Selecting the Year table
selec]