What is the best way to go about returning a result set with the distinct zone values as columns from the below basic select query?
SELECT weight, base_rate, zone
FROM Rates
WHERE modeId = 984
Here is the table:
Rates table:
id int
modeId int
base_rate decimal
zone varchar
weight decimal
I tried creating a pivot query to accomplish this, but so far have been unsuccessful. Any ideas? Should I just be manipulating the results in the C# code instead? The results will be put into a gridview.
This is what I would like the result set to look like
weight zone1 zone2 zone3 zone4
5 5.50 5.65 5.75 6.00
10 5.55 6.00 7.00 8.00
15 7.50 8.00 9.00 10.00
Here is my failed attempt at creating the pivot query.
select *
from
(
SELECT weight, base_rate, zone FROM Rates where modeId=984
) as d
pivot
(
max(weight)
for zone in (select distinct zone from d)
) piv;
SQL Server does not allow using a SELECT statement to get the list of columns for a PIVOT. The columns names must be know at run-time.
If you have a limited number of values, then you would need to hard code the query:
select weight, Zone1, Zone2, Zone3, Zone4
from
(
SELECT weight, base_rate, zone
FROM Rates where modeId=984
) as d
pivot
(
max(base_rate)
for zone in (Zone1, Zone2, Zone3, Zone4)
) piv;
But if you are going to have an unknown number of values, then you need to look at using dynamic SQL. This will create a sql string that will then be executed at run-time. The syntax will be similar to:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(zone)
FROM Rates
where modeId=984
group by zone
order by zone
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT weight,' + #cols + '
from
(
SELECT weight, base_rate, zone
FROM Rates where modeId=984
) x
pivot
(
max(base_rate)
for zone in (' + #cols + ')
) p '
execute sp_executesql #query;
try dynamic query as below.
declare #pivcols as varchar(200);
select #pivcols = stuff((select distinct '],[' + zone
from Rates order by '],[' + zone
for xml path('')
), 1, 2, '') + ']';
declare #pivquery varchar(500)
set #pivquery = 'Select '+
#pivcols +'
from
(select weight, base_rate, zone from rates where modeId=984
) as d
PIVOT
(
max(weight)
for zone in ( '+ #pivcols +' )
) AS pvt '
EXECUTE(#pivquery)
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
let's say i have 2 table
First one is "Orders"
Select * from Orders
give me this results.
Order_ID Date_Start Date_End Order_Name
2059 2020-11-13 00:00:00.000 2020-11-14 00:00:00.000 order1
2060 2020-12-12 00:00:00.000 2020-12-22 00:00:00.000 order2
and second table say it "Dates"
This is desired results for Dates table.i need to insert dates between two dates to that table for each order ID.
Date Type1 Type2 Type3 Type4 Type5 Order_ID
2020-11-13 00:00:00.000 NULL NULL NULL NULL NULL 2059
2020-11-14 00:00:00.000 NULL NULL NULL NULL NULL 2059
i hope this is more clear now.
Actually quite simple using OVER/PARTITION and then dateadd().
First, we need to get however many records you want in your final list of records. To do this, pick any table that has at least as many rows as you want. Could be an employee table, customers, orders, whatever. For your example, as long as it had 14 days. From that, lets just create a temp result set giving you a simple run of numbers 1 through whatever... 10, 14, 127, whatever, as long as the table has that many records.
Now, the partition by order by is part of the trick. You can't partition by constants, but you CAN do based on an equation. So, pick the "ID" column of whatever table and multiply by 0 will always give you 0. So your partitioning will group all values with an equated value of 0... Tricky huh... So now, all the records fall into this one group and get assigned a row number within that group. Finish that off with a "TOP 14", and you get your 14 records to start your list basis.
SELECT top 10
ROW_NUMBER() OVER(PARTITION BY SomeTableID * 0 order by SomeTableID * 0) AS "MyRow"
FROM
SomeTable
So now, I have a result set with 10 rows in it with the values running from 1 to 10.
Now, lets build the dates. As long as you are building consecutively, such as per day, per month, per year, or whatever pattern, use one date as your baseline and keep adding. In the sample below, I am using the current date and just keep adding 1 month, but again, you can do for days, weeks, whatever.
select
dateadd( month, Counter.MyRow, convert( date, getdate() )) ListOfDates
from
( SELECT top 10 ROW_NUMBER()
OVER(PARTITION BY SomeTableID * 0 order by SomeTableID * 0) AS "MyRow"
FROM SomeTable ) Counter
So, in the above example, it will return 10 rows starting with today and generate
2020-11-20
2020-12-20
2021-01-20
...
2021-08-20
FOLLOW-UP.
Your query is failing because you are explicitly concatenating strings to build your command... BAD technique. You should parameterize your queries. Build a SQL Command object, add parameters, and THEN call your fill.
var sqlcmd = new SqlCommand("", con);
sqlcmd.CommandText =
#"WITH theDates AS
(
SELECT #parmStartDate as theDate
UNION ALL
SELECT DATEADD(day, 1, theDate)
FROM theDates
WHERE DATEADD(day, 1, theDate) <= #parmEndDate
)
SELECT theDate
FROM theDates
OPTION(MAXRECURSION 0)";
sqlcmd.Parameters.AddWithValue("parmStartDate", dataGridView.CurrentRow.Cells[2] );
sqlcmd.Parameters.AddWithValue("parmEndDate", dataGridView.CurrentRow.Cells[3] );
var ds = new DataSet();
var dtbl2 = new DataTable();
// pass the pre-formatted and parameterized query command to the SQL Data Adapter
var sda2 = new SqlDataAdapter(sqlcmd);
Here is some sql to build a dynamic date range table. You will need to customize it for your needs in the /* replace with your column after join / and / Join your table sections */
/*
script to build table with dynamic columns
*/
DROP TABLE IF EXISTS #tempDateRange
DROP TABLE IF EXISTS #dateRangeTable
DECLARE #StartDate datetime = DATEADD(DAY, -14, GETDATE()),
#EndDate datetime = GETDATE()
/*
Generate date range table
*/
SELECT DATEADD(DAY, nbr - 1, #StartDate) AS [Date],
UPPER(LEFT(DATENAME(mm, DATEADD(DAY, nbr - 1, #StartDate)), 3)) AS [MonthShort],
MONTH( DATEADD(DAY, nbr - 1, #StartDate)) AS [Month],
YEAR(DATEADD(DAY, nbr - 1, #StartDate)) AS [Year],
CONCAT(UPPER(LEFT(DATENAME(mm, DATEADD(DAY, nbr - 1, #StartDate)), 3)), '-', YEAR(DATEADD(DAY, nbr - 1, #StartDate))) AS MonthYear
INTO #tempDateRange
FROM ( SELECT TOP(DATEDIFF(DAY, #StartDate, #EndDate)) ones.n + 10*tens.n + 100*hundreds.n + 1000*thousands.n AS Nbr
FROM (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) ones(n),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) tens(n),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) hundreds(n),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) thousands(n)
WHERE ones.n + 10*tens.n + 100*hundreds.n + 1000*thousands.n BETWEEN 1 AND DATEDIFF(DAY, #StartDate, #EndDate)
ORDER BY 1
) nbrs
WHERE nbr - 1 <= DATEDIFF(DAY, #StartDate, #EndDate)
/*
Generate columns for date range
*/
DECLARE
#columns NVARCHAR(MAX) = ''
SELECT #columns+=QUOTENAME(convert(nvarchar(10), Date, 120)) + ' NVARCHAR(10),'
FROM (
SELECT DISTINCT Date, [Month], [Year] FROM #tempDateRange
) x
ORDER BY x.[Year], x.[Month]
SET #columns = LEFT(#columns, LEN(#columns) - 1);
DECLARE #sql NVARCHAR(MAX) = ''
SET #sql = '
INSERT #dateRangeTable
SELECT *
FROM (
SELECT a.TestData AS Data, /* replace with your column after join */
convert(nvarchar(10), Date, 120) AS [Date]
FROM #tempDateRange [date]
/* Join your table */
LEFT JOIN (
SELECT ''start test'' AS TestData, CAST('''+CONVERT(NVARCHAR, #StartDate)+''' AS DATE) AS TargetDate
UNION
SELECT ''end test'' AS TestData, CAST('''+CONVERT(NVARCHAR, DATEADD(DAY, -1, #EndDate))+''' AS DATE) AS TargetDate
) AS a ON CAST(a.TargetDate AS DATE) = CAST(date.[date] AS DATE)
WHERE [date].[Date] BETWEEN CAST('''+CONVERT(NVARCHAR, #StartDate)+''' AS DATE) AND CAST('''+CONVERT(NVARCHAR, #EndDate)+''' AS DATE)
) o
PIVOT(
MAX(Data)
FOR [Date] IN ('+ REPLACE(#columns, 'NVARCHAR(10)', '') +')
) AS pivot_table;
'
SET #sql = N'
DROP TABLE IF EXISTS #dateRangeTable
CREATE TABLE #dateRangeTable('+#columns+')
' +
#sql
+ N'
SELECT * FROM #dateRangeTable
DROP TABLE IF EXISTS #dateRangeTable
'
PRINT (#sql)
--EXECUTE sp_executesql #sql
I have 2 tables SALM(Sales table) having filed BillNo,Date,Amount,CusId and CUSMAS(Customer table) having ID,CustomerName.I Want to create a report(between date) having Date on top and Sales details(from SALM) of that date below then Total sales Amount of that date below.
Inputs:From Date and To Date
I have tried this query:
(SELECT DISTINCT SALM.INVDATE AS RES1,'' AS RES2 ,'' AS RES3 FROM SALM
WHERE SALM.INVDATE BETWEEN #01-Jan-2018# AND #01-Mar-2019# ORDER BY
SALM.INVDATE) UNION (SELECT SALM.ORDNO AS RES1, ACCMAS.ACCNAME AS RES2,''
AS RES3 FROM SALM INNER JOIN ACCMAS ON SALM.CUSTCODE = ACCMAS.ID WHERE
SALM.INVDATE BETWEEN #01-Jan-2018# AND #01-Mar-2019# ORDER BY SALM.INVDATE
UNION select SUM(SALM.AMOUNT) AS RES1, sum(SALM.TAX) AS RES2,
sum(SALM.NTVALUE) AS RES3 FROM SALM WHERE SALM.INVDATE
BETWEEN #01-Jan-2018# AND #01-Mar-2019# group by SALM.INVDATE );
But the result is not getting in the format I needed
Report format is as shown in picture
You won't be able to do this in only one SQL-Expression.
When you use T-SQL (MSSSQL) then you can use this expression for one given date.
SELECT '' AS 'Bill No.', CONCAT('Date: ', '03.04.2018') AS 'Customer Name', '' AS 'Amount'
UNION ALL
SELECT CONVERT(varchar, BillNo), CustomerName, CONVERT(varchar, Amount)
FROM SALM LEFT JOIN CUSMAS ON SALM.CusId = CUSMAS.Id WHERE Date = '3.4.2018'
UNION ALL
SELECT '', 'Cash Total', 'don''t know what you want in herer'
UNION ALL
SELECT '', 'Credit Total', 'don''t know what you want in herer'
UNION ALL
SELECT '', 'Day Total', CONVERT(varchar, SUM(Amount))
FROM SALM LEFT JOIN CUSMAS ON SALM.CusId = CUSMAS.Id WHERE Date = '3.4.2018'
Then you could do this in C# throu a loop of all possible DateTimes and use the expresion for each DateTime.
When you just want to call one SQL function, then you need to create a FUNCTION which returns a table. You can read more about this here.
CREATE FUNCTION GetMyReport(#from DATETIME, #to DATETIME)
RETURNS #result TABLE
(
BillNo VARCHAR,
CustomerName VARCHAR,
Amount VARCHAR
)
BEGIN
--FILL YOUR TABLE HERE
END;
I have a table like below
Name Year Bonus
---- ----- ------
Ram 2011 1000
Ram 2011 2000
Shyam 2011 'No Bonus'
Shyam 2012 5000
I want to display the total bonus year wise for each person.I tried below query
SELECT [Year],[Ram],[Shyam] FROM
(SELECT Name, [Year] , Bonus FROM Employee )Tab1
PIVOT
(
SUM(Bonus) FOR Name IN (Ram,Shyam)) AS Tab2
ORDER BY [Tab2].[Year]
My Output Should be like below
Name 2011 2012
---- ------ ------
Ram 3000 0
Shyam 'No Bonus' 5000
But it is not working.
Can anyone help me on this?
If your dbms is sql-server you can try to use SUM condition aggregate function in a CTE
then use CAST with coalesce to make it.
;WITH CTE AS(
SELECT Year,Name,
SUM(CASE WHEN Bonus LIKE '%[0-9]%' THEN CAST(Bonus AS DECIMAL) ELSE 0 END) total,
COUNT(CASE WHEN Bonus = 'No Bonus' THEN 1 END) cnt
FROM T
GROUP BY Year,Name
)
SELECT Name,
coalesce(MAX(CASE WHEN Year = 2011 THEN CAST(total AS VARCHAR(50)) END),'No Bonus') '2011',
coalesce(MAX(CASE WHEN Year = 2012 THEN CAST(total AS VARCHAR(50)) END),'No Bonus') '2012'
FROM CTE
GROUP BY Name
sqlfiddle
If you want to create columns dynamically you can try to use dynamic PIVOT.
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
;WITH CTE AS(
SELECT Year,Name,
SUM(CASE WHEN Bonus LIKE '%[0-9]%' THEN CAST(Bonus AS DECIMAL) ELSE 0 END) total,
COUNT(CASE WHEN Bonus = 'No Bonus' THEN 1 END) cnt
FROM T
GROUP BY Year,Name
)
SELECT #cols = STUFF((SELECT distinct ',coalesce(MAX(CASE WHEN cnt > 0 and Year = ' + cast(Year as varchar(5)) + ' THEN ''No Bonus'' WHEN Year = ' + cast(Year as varchar(5)) + ' and cnt = 0 THEN CAST(total AS VARCHAR(50)) END),''0'')' + QUOTENAME(Year)
FROM CTE c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = '
;WITH CTE AS(
SELECT Year,Name,
SUM(CASE WHEN Bonus LIKE ''%[0-9]%'' THEN CAST(Bonus AS DECIMAL) ELSE 0 END) total,
COUNT(CASE WHEN Bonus = ''No Bonus'' THEN 1 END) cnt
FROM T
GROUP BY Year,Name
)
SELECT Name, ' + #cols + '
from CTE
GROUP BY Name'
exec(#query)
sqlfiddle
According to your problem the following query is what I understood. Not the ideal solution but this will do.
You can modify the query if you need to make it dynamic.
SELECT [Name]
, case when [2011] = 0 then 'No Bonus' when [2011] is null then '0' else cast([2011] as varchar(50)) end as [2011]
, case when [2012] = 0 then 'No Bonus' when [2012] is null then '0' else cast([2012] as varchar(50)) end as [2012]
FROM
(SELECT Name, [Year] , cast(Bonus as int) Bonus FROM Employee)Tab1
PIVOT
(
SUM(Bonus) FOR Year IN ([2011],[2012])) AS Tab2
ORDER BY [Tab2].[Name]
You need to pass 0 in the table though and then modify in the PIVOT
I would just give up on storing numbers as strings. I don't see the difference between 0/NULL and 'No Bonus', except that the latter makes queries prone to really bad type conversion problems.
So, my advice is to do:
SELECT [Year],[Ram],[Shyam]
FROM (SELECT Name, [Year], TRY_CONVERT(int, Bonus) as Bonus
FROM Employee
) e
PIVOT (SUM(Bonus) FOR Name IN (Ram, Shyam)) AS Tab2
ORDER BY [Tab2].[Year] ;
You probably don't like that solution -- although I really do strongly recommend it because I have spent way too many hours debugging problems with numbers and dates stored as strings.
So, if you persist with storing values as string, use conditional aggregation and a bunch of logic:
select year,
coalesce( sum(case when name = 'Ram'
then convert(varchar(255), try_convert(int, bonus))
end),
'No Bonus'
) as Ram,
coalesce( sum(case when name = 'Shyam'
then convert(varchar(255), try_convert(int, bonus))
end),
'No Bonus'
) as Shyam
from employee e
group by year
order by year;
This question already has answers here:
How to use sp_executesql to avoid SQL Injection
(2 answers)
Closed 5 years ago.
My table data is like this:
enter image description here
I'm using this query:
DECLARE #cols AS NVARCHAR(MAX), #query AS NVARCHAR(MAX)
SELECT #cols = STUFF((SELECT distinct ','+ QUOTENAME('COMPLETE_' + cast(row_number() over(partition by CID order by CID) as varchar(10)))
FROM allleads
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
SET #query = 'SELECT CustomerName, address, CID, ' + #cols +
' FROM (SELECT CustomerName, address, CID, COMPLETE,''COMPLETE_''+ CAST(row_number() over (partition by CID order by RecordDate) as varchar(10)) val from allleads) x PIVOT (MAX(COMPLETE) for val in (' + #cols + ')) p ' execute(#query)
is working fine.
But when I add a search condition between date like this:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX),
#dt AS VARCHAR(10),
#dt1 AS VARCHAR(10) set
#dt='2017/05/18' set
#dt1='2017/07/10'
select #cols = STUFF((SELECT distinct ','+
QUOTENAME('COMPLETE_' +
cast(row_number()
over(partition by CID order by CID) as varchar(10)))
from allleads FOR XML PATH(''),
TYPE).value('.', 'NVARCHAR(MAX)'),1,1,'')
set #query = 'SELECT CustomerName,address,CID, ' +
#cols + ' from (select CustomerName,address,CID, COMPLETE,''COMPLETE_''+
cast(row_number()
over(partition by CID order by RecordDate) as varchar(10))
val from allleads **where convert(varchar(10),RecordDate,111) between '+#dt+' and '+#dt1+'**) x pivot(max(COMPLETE) for val in (' + #cols + ')) p ' execute(#query)
then it is showing an error:
Msg 245, Level 16, State 1, Line 1
Conversion failed when converting the varchar value '2017/05/19' to data type int.
Please help.
Thanks in advance.
You should really use parameters for constants in a query . . . and use sp_executesql to pass them in.
In your case, the problem is missing quotes around the dates. Instead of:
between '+#dt+' and '+#dt1+'
You can do:
between '''+#dt+''' and '''+#dt1+'''
In other words, the error is occurring when you execute the code, not when you are defining the strings.
You should also learn to write your code in a less sloppy manner. I don't see how even you can read it, much less anyone else.
I have a c# web page and i am using SQL for the database
i need to display the data from table into a gridview
SQL : I have a table as below
Exec_Date Plat Pass Fail
----------------------------------------------------------------
2017-02-19 12:32:43.570 MSSQL 10 12
2017-02-19 12:32:43.570 MSSQL 10 12
2017-02-18 12:32:43.570 Sybase 10 12
2017-02-18 12:32:43.570 Oracle 10 12
i would like to convert the table into a customised format for a presentation
Status 18/02 19/02
-------------------------
Pass 20 20
Fail 24 24
is the above possible to be done by Pivot ? if so can anyone give some idea on that pls?
I have tried the below so far and the #Names is not resolving the variable value in query
create table #TempTable
(Exec_Date varchar(max),
Pass int,
Ref_Status varchar(max)
)
Insert into #TempTable
select cast(Exec_Date as DATE) as Date,SUM(Pass) as Pass,'PASS' from F_Exec where cast(Exec_Date as DATE) >= '2017-02-17' and cast(Exec_Date as DATE) <= '2017-02-20' group by Exec_Date,Pass
DECLARE #Names varchar(max)
SELECT #Names = COALESCE(#Names + '],[', '[') + Exec_Date FROM #TempTable
select #Names=#Names+']'
Select * from #TempTable pivot
(SUM(Pass) for Exec_Date IN (#Names) )as PivotTable
The variable #Names is not considered as variable. If i manually replace that resultant value from the variable it works
Try this code:
declare #tbl table (Exec_Date datetime, Plat varchar(max), Pass int, Fail int)
insert into #tbl(Exec_Date , Plat , Pass , Fail ) values
('2017-02-19 12:32:43.570', 'MSSQL',10,12),
('2017-02-19 12:32:43.570', 'MSSQL',10,12),
('2017-02-18 12:32:43.570', 'Sybase',10,12),
('2017-02-18 12:32:43.570', 'Oracle',10,12)
select * from
(
select Date,Status,sum(Success) success from
(
select left(convert(varchar(20),Exec_Date,103),5) as [Date],Pass,Fail from #tbl
) as baseData
unpivot
(
Success for Status in (Pass,Fail)
) as tblUnPivot
group by date ,status
) as baseData
pivot
(
sum(success)
for Date in ([18/02],[19/02])
) as tblPivot
order by Status
I got the solution
declared nvarchar variable and concatenated the query & dynamic column names
DECLARE #sqlstring nvarchar(MAX)
SET #sqlstring =N'
Select * from #TempTable pivot (SUM(Pass) for Exec_Date IN (' +#Names+') )as PivotTable;';
EXEC sp_executesql #sqlstring;