Dynamic SQL column names - sql

I have a query like:
SELECT [Week 1].Product, [Week 2].Product, [Week 3].Product, [Week 4].Product, Sum([Week 1].Transaction_Amount), Sum([Week 2].Transaction_Amount), Sum([Week 3].Transaction_Amount), Sum([Week 4].Transaction_Amount)
FROM [Week 2],[Week 3],[Week 3],[Week 4];
I have data for 70 weeks name [week 1] to [week 70]
Is it possible to make [week 1],[Week 2],[Week 3],[Week 4] dynamic.
(i,e) have a master table where I can have 4 week names like [Week 8], [Week 6], [Week 45], [Week 18] and replace the [Week 1], [Week 2], [Week 3], [Week 4] with the above 4 in my query
IT IS AN MS ACCESS APPLICATION. Sorry I did not mention previously.

Yes you can do that using a dynamic query and then using EXEC or sp_executesql to execute the query
The following will be a pseudo code which you can improvise as per your requirement.
DECLARE #STartCount, #TableCount, #Query, #SubQuery, #WeekTblName
SET #StartCount = 1
SELECT #TableCount = Count(*) FROM Master_Table
WHILE(#StartCount <= #TableCount)
BEGIN
SELECT #WeekTblName = Col_Name FROM Master_Table WHERE ID = #StartCount
SET #SubQuery = #SubQuery + #WeekTblName + ' AS [Week ' + #STartCount + '], '
SET #StartCount = #StartCount + 1
END
SET #SUBQUERY = LEFT(#SUBQUERY, LEN(#SUBQUERY) - 2);
SET #Query = 'SELECT [Week 1].Product, [Week 2].Product, [Week 3].Product, [Week 4].Product, Sum([Week 1].Transaction_Amount), Sum([Week 2].Transaction_Amount), Sum([Week 3].Transaction_Amount), Sum([Week 4].Transaction_Amount)
FROM ' + #SubQuery
EXEC(#Query)
Hope this helps

UPDATE
Create some VBA code on a button which dynamically creates the select query. How to build a dynamic query
You could:
1) Change your table structure so that you have a single Table for all weeks e.g. Week (ID, WeekNumber, column1, column2...). And then in your Select statement you can add a where clause to use a parameter to select which weeks you are interested in. This is dependant on all the weeks tables having the same structure.
2) Use Dynamic SQL to build your select statement and replace your table names. See sp_executesql

Related

SQL Case Expression with a Where Clause

I have the following example Data set that was created by the following query that sets an employee as "Active' or 'Inactive" based on if they worked any hours during a month.
Select Concat([First Name],' ', [Last Name]) AS 'FullName',
CASE (SUM([Week 1] + [Week 2] + [Week 3] + [Week 4]))
When 0 Then 'Inactive'
ELSE 'Active'
END [Status]
From dbo.hours
Group by [first name], [last name]
FullName
Status
Alan Brewer
Active
Alejandro McGuel
Inactive
Alex Nayberg
Active
Im trying to get rid of all the 'Active' Status rows so that my query only displays those who are 'inactive'. I attempted to add WHERE Status = Inactive between the FROM and GROUP BY expression but it returns no results. (Does not give an error, just returns empty columns).
Anyone know what I am missing?
You can't add a WHERE condition on the Status column just like that since it's not available to WHERE clause .. you can use that query as inline view and do the condition check in outer query like
select * from (
Select Concat([First Name],' ', [Last Name]) AS 'FullName',
CASE (SUM([Week 1] + [Week 2] + [Week 3] + [Week 4]))
When 0 Then 'Inactive'
ELSE 'Active'
END [Status]
From dbo.hours
Group by [first name], [last name] ) xxx
where Status != 'Active';
You need a HAVING clause, but also for your requirement you don't need the CASE expression:
SELECT CONCAT([First Name],' ', [Last Name]) AS FullName,
'Inactive' [Status]
FROM dbo.hours
GROUP BY [first name], [last name]
HAVING SUM([Week 1] + [Week 2] + [Week 3] + [Week 4]) = 0
CTE will work as well:
with cte as(
Select Concat([First Name],' ', [Last Name]) AS 'FullName',
CASE (SUM([Week 1] + [Week 2] + [Week 3] + [Week 4]))
When 0 Then 'Inactive'
ELSE 'Active'
END [Status]
From dbo.hours
Group by [first name], [last name])
select FullName from cte where Status <> 'Active'

Incrementing a variable inside a looped INSERT in SQL Server 2012

How can we be able to include the other column to loop the same as the looped month in this code?
What I'm trying to achieve is the range of $datestart and $dateend should loop the [Data 3] as well.
If [Data 3] is equal to 100, and the range of $datestart and $dateend is 10
Data 3 column should increment from 100,101,102,103,104,105.. and so on up to 110
DECLARE #MYTESTTABLE TABLE
(
[Month] DATE,
[Data 1] INT,
[Data 2] INT,
[Data 3] INT
);
WITH MYLOOP AS
(
SELECT CAST('$datestart' AS DATE) AS [date], '$datavalue' AS [Data 3]
UNION ALL
SELECT DATEADD(MONTH, 1, [date]), [Data 3] = [Data 3] + 1
FROM MYLOOP
WHERE DATEADD(MONTH, 1, [date]) < CAST('$dateend' AS DATE)
)
INSERT INTO #MYTESTTABLE ([Month], [Data 1], [Data 2], [Data 3])
SELECT
LEFT([date], 10) AS [Month], 100 AS [Data 1], 100 AS [Data 2], [Data 3]
FROM
MYLOOP
SELECT *
FROM #MYTESTTABLE
Suppose $datavalue is equal to 100, and it will loop along with the range of the loop from $datestart to $dateend.
Example
The next column (Data 3) should be 100,101,102,103,104,... and so on
How can we achieve this?
I tried doing it like this [Data 3] = [Data 3] + 1 but I get this error:
Types don't match between the anchor and the recursive part in column "Data 3" of recursive query "MYLOOP".
The error message just happens to be the first error that SQL Server picks. There is in fact a great deal wrong with your SQL that SQL Server is not reporting.
Firstly T-SQL uses #variablename to denote variables (not $variablename). Secondly enclosing something in single quotes (') will cause SQL Server to treat it as a literal string. If you enter the following in a Query window:
SELECT CAST('$datestart' as date)
you will get a conversion failed error.
In addition I strongly recommend that you strongly type your variables #datestart and #dateend so that you do not need to use casts.
As an example try the following:
DECLARE #datestart date = '2019-01-01'
DECLARE #dateend date = '2019-11-02'
DECLARE #datavalue int = 100
DECLARE #MYTESTTABLE TABLE ([Month] date, [Data 1] INT, [Data 2] INT, [Data 3] INT);
;WITH MYLOOP AS (
SELECT #datestart AS [date], #datavalue as [Data 3]
UNION ALL
SELECT DATEADD(MONTH, 1, [date]), [Data 3]=[Data 3]+1
FROM MYLOOP
WHERE DATEADD(MONTH, 1, [date]) < #dateend)
INSERT INTO #MYTESTTABLE ([Month], [Data 1], [Data 2], [Data 3])
SELECT [date] AS [Month], 100 AS [Data 1], 100 AS [Data 2], [Data 3]
FROM MYLOOP;
SELECT * FROM #MYTESTTABLE
This will give you data from 2019-01-01 to 2019-11-01. Note I set #dateend as 2019-11-02, because you are using < #dateend. This ensures that you will also get the desired last date. Otherwise you can use <=.

MSSQL Order by date with distinct

I have this query segment below where I'm trying to build a string of "month-year" from a date field in this table. It's very important that it comes in the right order starting from current month going forward 12 months.
DECLARE #cols AS NVARCHAR(MAX)
SELECT #cols = STUFF(
(SELECT N',' + QUOTENAME(y) AS [text()]
FROM (SELECT DISTINCT CONVERT(char(3), StartDate, 0) + '-' +
RIGHT(CONVERT(varchar, YEAR(StartDate)), 2) AS y
FROM Products2
) AS Y
--ORDER BY y desc
FOR XML PATH('')),
1, 1, N'')
This query isn't pulling the dates in the right order and I wanted to see if you guys know of any neat tricks to pull the dates in the correct order. I can bring in the startDate column and sort it by that but it brings in duplicates as it may have several entries for the same month. I've created a sample table here http://sqlfiddle.com/#!6/3a500/5
You could use
DECLARE #cols AS NVARCHAR(MAX);
SELECT #cols = STUFF((SELECT N',' + QUOTENAME(y) AS [text()]
FROM (SELECT CONVERT(CHAR(3), StartDate, 0) + '-'
+ RIGHT(CONVERT(VARCHAR, YEAR(StartDate)), 2) AS y,
MIN(StartDate) AS z
FROM Products2
GROUP BY CONVERT(CHAR(3), StartDate, 0) + '-'
+ RIGHT(CONVERT(VARCHAR, YEAR(StartDate)), 2)) AS Y
ORDER BY z
FOR XML PATH('')), 1, 1, N'');
SELECT #cols;
SQL Fiddle
It looks like you just getting the month/year so we can truncate to the first day of the month and include that in the query.
DATEADD(month, DATEDIFF(month, 0, StartDate), 0) monthStart
Now we can order by it:
DECLARE #cols AS NVARCHAR(MAX);
SELECT #cols = STUFF(
(SELECT N',' + QUOTENAME(y) AS [text()]
FROM (
SELECT DISTINCT CONVERT(char(3), StartDate, 0) + '-' +
RIGHT(CONVERT(varchar, YEAR(StartDate)), 2) AS y,
DATEADD(month, DATEDIFF(month, 0, StartDate), 0) monthStart
FROM Products2
) AS Y
ORDER BY monthStart
FOR XML PATH('')),
1, 1, N'');
select #cols;
This is the output:
[Dec-15],[Jan-16],[Feb-16],[Mar-16],[Apr-16],[May-16],[Jun-16],[Jul-16],[Aug-16],[Sep-16],[Oct-16],[Nov-16],[Dec-16]
Is that what you are looking for? Here is a fiddle:
http://sqlfiddle.com/#!6/3a500/67
Better yet, just select the distinct month start dates and then only do the string conversion on that.
DECLARE #cols AS NVARCHAR(MAX);
SELECT #cols = STUFF(
(SELECT N',' + QUOTENAME(CONVERT(char(3), monthStart, 0) + '-' +
RIGHT(CONVERT(varchar, YEAR(monthStart)), 2)) AS [text()]
FROM (
SELECT DISTINCT
DATEADD(month, DATEDIFF(month, 0, StartDate), 0) monthStart
FROM Products2
) AS Y
ORDER BY monthStart
FOR XML PATH('')),
1, 1, N'');
select #cols;
Here is that fiddle:
http://sqlfiddle.com/#!6/3a500/72
If you are using SQL Server 2012+ you could use FORMAT function:
DECLARE #cols AS NVARCHAR(MAX);
;WITH cte AS -- get only one date per month/year
(
SELECT MIN(StartDate) AS StartDate
FROM #Products2
GROUP BY YEAR(StartDate),MONTH(StartDate)
)
SELECT #cols = STUFF((SELECT ',' + QUOTENAME(FORMAT(StartDate, 'MMM-yy'))
FROM cte
ORDER BY StartDate
FOR XML PATH('')),
1, 1, N'');
SELECT #cols;
LiveDemo
Output:
╔══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╗
║ result ║
╠══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╣
║ [Dec-15],[Jan-16],[Feb-16],[Mar-16],[Apr-16],[May-16],[Jun-16],[Jul-16],[Aug-16],[Sep-16],[Oct-16],[Nov-16],[Dec-16] ║
╚══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝

How to create Temp Table with month columns based on date parameters?

I have a stored procedure that accepts two Dates. In my stored procedure, I need to create a temp table with the months in between the two dates as columns.
For example,
If the user passes in
1/1/2016 , 8/1/2016
I need a temp table with the columns:
January February March April May June July August
How could I create this type of temp table with columns created in this manner? With the columns being based on the two dates passed in?
The following script should get you started (and almost there):
declare #start_date DATE = '20160101'
declare #end_date DATE = '20160801'
;WITH CTE AS
(
SELECT #start_date AS cte_start_date, DATENAME(month, #start_date) AS Name,
CAST(' ALTER TABLE #myTemp ADD ' + DATENAME(month, #start_date) + ' INT ' + CHAR(13) + CHAR(10) AS VARCHAR(8000)) AS SqlStr
UNION ALL
SELECT DATEADD(MONTH, 1, cte_start_date), DATENAME(month, DATEADD(MONTH, 1, cte_start_date)) AS Name,
CAST(SqlStr + ' ALTER TABLE #myTemp ADD ' + DATENAME(month, DATEADD(MONTH, 1, cte_start_date)) + ' INT ' + CHAR(13) + CHAR(10) AS VARCHAR(8000))
FROM CTE
WHERE DATEADD(MONTH, 1, cte_start_date) <= #end_date
)
SELECT cte_start_date, Name, SqlStr
FROM CTE
Using a recursive-CTE it generates a loop between start and end date, and for each month it computes its string representation and also creates a alter script to add the columns to a temporary table.
The CTE computes the SQL script gradually, so that the final script is on the last line.
Try This ....
declare #start_date DATE = '20160101'
declare #end_date DATE = '20160801'
;WITH CTE AS
(
SELECT #start_date AS cte_start_date, DATENAME(month, #start_date) AS NAME , 0 AS Coun
UNION ALL
SELECT DATEADD(MONTH, 1, cte_start_date), DATENAME(month, DATEADD(MONTH, 1, cte_start_date)) AS NAME , 0 AS Coun
FROM CTE
WHERE DATEADD(MONTH, 1, cte_start_date) <= #end_date
)
SELECT Coun,Name
INTO #tmp1
FROM CTE
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + Name
from #tmp1
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT ' + #cols + ' from
(
select Coun,Name
from #tmp1
) x
pivot
(
MAX(Coun)
for Name in (' + #cols + ')
) p '
execute(#query);
DROP TABLE #tmp1
It Will Return OutPut Like your expected output .......

How can I assign a normal table from a dynamic pivot table?

I need to make a report in Stimulsoft from the last 12 months from our current date, I used dynamic pivot table to make this, the original table is at figure 1
Then the pivot table is like the figure 2 (bigger image link here: http://i.stack.imgur.com/LPCuP.jpg)
The DACP_Value at figure 1 is the row at the date it corresponds at figure 2. Note that the culture is set to pt-BR (brazil)
Here is a sample code of the generation of the pivot table made in SQLFiddle
http://www.sqlfiddle.com/#!3/3205a/23
I need to put this dynamic data with the headers inside a normal table (it can be a temporary table) so I can use it in my report query and be recognized by the Stimulsoft software.
Add INTO YourTable after the SELECT on your code:
DECLARE #Col NVARCHAR(MAX) =
( SELECT ', ' + QUOTENAME(CONVERT(VARCHAR, DATEADD(MONTH, DATEDIFF(MONTH, 0, CURRENT_TIMESTAMP) - (12 - Number), 0), 103)) + ' = [' + CAST(number AS VARCHAR) + ']'
FROM Master..spt_values
WHERE Type = 'P'
AND number BETWEEN 0 AND 12
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
DECLARE #SQL NVARCHAR(MAX) =
N'WITH Data AS
( SELECT DACP_ID,
DACP_Value,
[MonthNum] = 12 - DATEDIFF(MONTH, DACP_Date, CURRENT_TIMESTAMP)
FROM yourtable
WHERE DATEDIFF(MONTH, DACP_Date, CURRENT_TIMESTAMP) BETWEEN 0 AND 12
)
SELECT DACP_ID' + #Col + '
INTO YourTable --Add this line here
FROM Data
PIVOT
( SUM(DACP_Value)
FOR MonthNum IN ([0], [1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12])
) pvt;'
EXECUTE SP_EXECUTESQL #SQL
As a way, you can use the master-detail report in Stimulsoft tool.
As master table you can use the table with date only:
SELECT CONVERT(DATE, DATEADD(MONTH, DATEDIFF(MONTH, 0, CURRENT_TIMESTAMP) - (12 - Number), 0), 103) AS DT,
CONVERT(VARCHAR(2),MONTH(DATEADD(MONTH, DATEDIFF(MONTH, 0, CURRENT_TIMESTAMP) - (12 - Number), 0))) + CONVERT(VARCHAR(4),YEAR(DATEADD(MONTH, DATEDIFF(MONTH, 0, CURRENT_TIMESTAMP) - (12 - Number), 0))) As MonthYear
FROM Master..spt_values
WHERE Type = 'P'
AND number BETWEEN 0 AND 12
The detail table it's "yourTable" with additional column:
select *, convert(varchar(2),Month(DACP_date)) + convert(varchar(4),Year(DACP_date)) as MonthYear from yourtable
realtion on MonthYear columns.
In report you can use the Cross-Data component for master data, and DataBand component for detail data.
Please see the image from the following link:
http://imgur.com/Ve03BXU
Also this is a solution for the case when the amount of data for each date is the same, ie ID for each date are the same. If not, one may have to add more conditions for showing headers on the left side.