Create report using by union query - c#

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;

Related

Insert all dates between start and end date into table [closed]

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

Get rolling 12 months data in SQL Server

I have a requirement to get the data for rolling next 12 months. This comes with a special condition to show the months with blank data if data does not exist in the database. For example - If data exists only till Jan 2017 then the result query should show Feb 2017 also but with blank data.
Using below query I am able to get the data which exists. I don't want to add any loops to check which month is missing and add those months.
SELECT
SiteCode
,CustomerName
,CalYear
,CalMonth
,CalDay
, CONVERT(DATE,CONVERT(VARCHAR(4),CalYear)+'-'+CONVERT(VARCHAR(2),CalMonth)+'-'+CONVERT(VARCHAR(2),CalDay)) AS CalDate
,MachineDownTimes
,MaterialsDownTimes
,LineBalancingLost
,Others
FROM
dbo.ProcessBackend
WHERE CustomerName = 'ZAS' AND SiteCode = 'HU01'
AND DATEFROMPARTS(CALYEAR, CALMONTH, CALDAY)
BETWEEN DATEFROMPARTS(DATEPART(YEAR, GETDATE()), DATEPART(MONTH, GETDATE()), 1) AND
DATEFROMPARTS(DATEPART(YEAR, (DATEADD(MONTH, 12, GETDATE()))), DATEPART(MONTH, (DATEADD(MONTH, 12, GETDATE()))), 1)
What would be the best option to achieve this in SQL or C#
As already mentioned, these are the steps to achieve your goals:
1) create a months table, since you want to display month data even if data is not present. You can choose something from these answers - I have adapted the shortest and elegant solution from there:
declare #today DATE = GETDATE()
declare #thisMonth DATE = DATEFROMPARTS(YEAR(#today), MONTH(#today), 1)
declare #startMonth DATE = DATEADD(month, -11, #thisMonth)
PRINT 'This month '; PRINT #thisMonth
PRINT 'Start month '; PRINT #startMonth
declare #monthInfo TABLE (BOM DATE)
insert into #monthInfo (BOM)
select top 11 dateadd(month, num, #startMonth) as mon
from (select row_number() over (order by (select 1)) as num
from sys.columns c
) n cross join
(select #startMonth firstdate) const
SELECT * FROM #monthInfo
The results is:
BOM
2015-05-01
2015-06-01
2015-07-01
2015-08-01
2015-09-01
2015-10-01
2015-11-01
2015-12-01
2016-01-01
2016-02-01
2016-03-01
2) create final query by selecting from generated dates and your table
DECLARE #CustomerName VARCHAR(100) = 'ZAS'
DECLARE #SiteCode VARCHAR(32) = 'HU01'
;WITH CTE AS (
SELECT SiteCode, CustomerName, DATEFROMPARTS(CalYear, CalMonth, 1) AS CalMonth,
MachineDownTimes, MaterialsDownTimes, LineBalancingLost, Others
FROM dbo.ProcessBackend
WHERE CustomerName = #CustomerName AND #SiteCode = #SiteCode
)
SELECT #CustomerName, #SiteCode, M.BOM,
SUM(MachineDownTimes), SUM(MaterialsDownTimes), SUM(LineBalancingLost), SUM(Others)
FROM #monthInfo M
LEFT JOIN CTE ON CTE.CalMonth = M.BOM
GROUP BY CTE.CustomerName, CTE.SiteCode, M.BOM
Create default month table which included (Jan -> Dec).
Union default month table with your output table and SUM (those columns you can to show) these column:
(MachineDownTimes
,MaterialsDownTimes
,LineBalancingLost
,Others)
Done.
Reference : Fill empty dates in a matrix SSRS

Order By Date not working in my project

I am working on a C# / ASP.NET project and I am using SQL Server as my back end.
I have written a query to get the details of 'Total Sale' per month and year, i.e in mm-yyyy format and I want to sort it ascending order.
But the query does not sort its results in ascending order.
My query is
/* TOTAL SALE*/
SELECT
CAST((Datepart(Month, [p].Transdate)) AS varchar(50)) +'-'+
CAST((Datepart(Year, [p].Transdate)) AS varchar(50)) AS [Month/Year],
SUM([p].Litres) [Total Sale]
FROM
CustomerPoints AS [p]
INNER JOIN
Customers AS [c] ON [c].[CustomerID] = [p].[CustomerID]
WHERE
[p].Transdate BETWEEN '2013-01-20' AND '2015-03-05'
AND [c].DistributorID = '1'
GROUP BY
CAST((Datepart(Month, [p].Transdate)) AS varchar(50)) + '-'+
CAST((Datepart(Year, [p].Transdate)) AS varchar(50))
ORDER BY
CAST((Datepart(Month, [p].Transdate)) AS varchar(50)) + '-'+
CAST((Datepart(Year, [p].Transdate)) AS varchar(50)) ASC
The output of above query is
Month/Year Total Sale
--------------------------
1-2013 600
1-2014 2300
10-2014 0
2-2015 1560
3-2014 80
3-2015 700
Kindly help.
Thank you in advance.
Because you cast the dates to strings you get lexicographical order instead of by date. So don't cast it:
SELECT Cast((Datepart(month, [p].transdate)) AS VARCHAR(50))
+ '-'
+ Cast((Datepart(year, [p].transdate))AS VARCHAR(50)) AS [Month/Year],
Sum([p].litres) [Total Sale]
FROM customerpoints AS [p]
INNER JOIN customers AS [c]
ON [c].[customerid] = [p].[customerid]
WHERE [p].transdate BETWEEN '2013-01-20' AND '2015-03-05'
AND [c].distributorid = '1'
GROUP BY Cast((Datepart(month, [p].transdate)) AS VARCHAR(50))
+ '-'
+ Cast((Datepart(year, [p].transdate))AS VARCHAR(50))
ORDER BY MIN(transdate) ASC
But since you are using Group By you have to tell SQL-Server from what row of each group you want to take the transdate. I have simply used MIN(transdate) which is the beginning of the month.
Why do you "format" the date that you want to order by?
Cast((Datepart(Month,[p].Transdate)) as varchar(50)) + '-'+
Cast((Datepart(Year,[p].Transdate))as varchar(50)) ASC
This will order regarding that part as a varchar(50). Just order by Transdate.

advice for SQL query with row values as columns

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)

Execute multiple aggregates at once (SQL, LINQ).. or with better performance?

I have a table with records which include a datetime column "CreationDate".
I need to get the following information for every of the last 90 days:
How many records were there in total in existence
How many records were added on that day
I could do this through a loop of counting of course, but this would hit the database 90 times... is there a better way of doing this aggregate without having to riddle the DB with requests?
I'm using C#, LINQ, SQL Server 2008.
Are you looking for something like this?
WITH CTE AS
(SELECT COUNT(*) OVER () AS TotalCount,
CAST(CONVERT(VARCHAR, CreationDate, 101) as DATETIME) as DateValue, *
FROM MyTable
WHERE CreationDate >= DATEADD(DD, -90, GETDATE())
)
SELECT DateValue, TotalCount, COUNT(*) as RowCount
FROM CTE
group by DateValue, TotalCount
order by DateValue
;
Pull the records (or just the ids and creation dates, if that is all you need), and then perform the logic in code. One SELECT against the DB.
edit
In response to comment:
You can get the number of items for each day with a query like this:
SELECT CreationDate, COUNT(CreationDate) FROM MyTable GROUP BY CreationDate
Note that this assumes no times in CreationDate. If you have different times, the grouping won't work -- you'll have to flatten those out.
You can also add a WHERE clause to only look at the items from the last 90 days.
Bringing back the daily totals for the 90 days then aggregating in your application would probably be the best idea. There is currently no particularly satisfactory way of calculating running totals in SQL Server. An example of how you could do it though is below (using sys.objects as the demo table)
IF OBJECT_ID('tempdb..#totals') IS NOT NULL
DROP TABLE #totals
DECLARE #EndDate DATE = CURRENT_TIMESTAMP;
DECLARE #StartDate DATE = DATEADD(DAY,-89,#EndDate);
WITH DateRange AS
(
SELECT
#StartDate [DATE]
UNION ALL
SELECT
DATEADD(DAY, 1, DATE) [DATE]
FROM
DateRange
WHERE
DATE < #EndDate
)
SELECT DATE,COUNT(t.modify_date) AS DailyTotal
INTO #totals
FROM DateRange LEFT JOIN sys.objects t
ON modify_date BETWEEN #StartDate AND #EndDate
AND CAST(t.modify_date AS DATE) = DateRange.Date
GROUP BY DATE
ORDER BY DATE
DECLARE #BaseNumber INT = (SELECT COUNT(*) FROM sys.objects WHERE
modify_date < #StartDate);
SELECT t1.Date,
t1.DailyTotal,
#BaseNumber + SUM(t2.DailyTotal) AS RunningTotal
FROM #totals t1
JOIN #totals t2 ON t2.date <= t1.date
/*Triangular join will yield 91x45 rows that are then grouped*/
GROUP BY t1.Date,t1.DailyTotal
ORDER BY t1.Date

Categories