Please help out newbie.
I am reading mothly sales statistics for last two years from stored procedure, display it on asp.net site and it works just fine.
Problem is with products that are not sold often I need to figure out which months do not have any sales. In that case I need to put zero in table cell and move to next row in dataset.
For...Each does not do the trick in case where there isn't data for every month.
Question is, how to move to next sqlrow and how to test when all rows heve been read?
sqlSelect = "EXECUTE dealer_sales_statistics #productID = '" & strProdID.Value & "'"
Dim sqlConn As New SqlConnection(sqlConnStr)
Dim sqlRow As DataRow
sqlConn.Open()
Dim sqlAdapt As New SqlDataAdapter(sqlSelect,sqlConn)
Dim sqlDataSet As New DataSet()
sqlAdapt.Fill(sqlDataSet, "sales_statistics")
Do Until sqlRow.EOF
If intCounter < 12 Then
' arrMonth contains last 24 months, e.g. "12_2009" to "1_2008"'
' stored procedure can return values for any month between that range'
' amount of returned values (DataSet sqlRows) can vary from 0 to 24'
If arrMonth(intCounter) = sqlRow("month") & "_" & sqlRow("year") Then
strLine_1 &= "<td>" & CInt(sqlRow("qty")) & "</td>"
arrSumma_1 = arrSumma_1 + CInt(sqlRow("qty"))
sqlRow.MoveNext
Else
strLine_1 &= "<td class='cell'>0</td>"
End If
Else
'using intCouter and same code to separate sales in 12 month periods'
If arrMonth(intCounter) = sqlRow("month") & "_" & sqlRow("year") Then
strLine_2 &= "<td>" & CInt(sqlRow("qty")) & "</td>"
arrSumma_2 = arrSumma_2 + CInt(sqlRow("qty"))
sqlRow.MoveNext
Else
strLine_2 &= "<td>0</td>"
End If
End If
intCounter = intCounter + 1
Loop
I think that you are focusing on the wrong area by trying to do this in your code. I can think of a likely solution there but it is really messy. Instead, focus on making sure that the sets returned by the stored proc are complete so you can iterate them without worry about missing months. That is, the stored procedure is probably returning sets made up only of months where there were sales (e.g. due to an inner join) - and you need to change this so it returns all months.
So, instead of posting the VB code, I'd suggest that you post the stored proc to get help in resolving the issue.
As a general guideline, I'd approach this by creating a dummy table with the months of the year listed (along with their month numbers to perform the join). Then, fold that table in with the query using a left outer join to ensure that all months are represented. Also, when selecting the final sales figures, make sure that there are no null values (for months where there were no sales) by using an "IsNull(Val, 0) as Val" to substitute a zero.
Again, this is just general guidance, we'd need to see the actual sproc to really help.
Here is how I did solve this with SQL. I create dynamically temp table that holds last 24 months and another temp table with sales data 0 to 24 months. Maybe this will help somebody with similar problem. (code below is in sql server as stored procedure). Thank you for help Mark!
DECLARE #strTemp_months TABLE
(
sorting INT,
months INT,
years INT
)
DECLARE #mnth INT
SET #mnth = 0
WHILE (#mnth < 24)
BEGIN
INSERT #strTemp_months
SELECT CASE WHEN YEAR(GETDATE()) = YEAR(DATEADD( m , -#mnth , GETDATE())) THEN 1 ELSE 2 END AS sorting,
MONTH(DATEADD( m , -#mnth , GETDATE())), YEAR(DATEADD( m , -#mnth , GETDATE()))
SET #mnth = #mnth + 1
END
DECLARE #productID VARCHAR(12)
SET #productID = '1234567890'
DECLARE #strTemp_statistics TABLE
(
sorting INT,
months INT,
years INT,
productno VARCHAR(35),
salesqty DECIMAL(9,2)
)
INSERT #strTemp_statistics
SELECT CASE WHEN YEAR(transaction_date) = YEAR(GETDATE()) THEN 1 ELSE 2 END AS sorting,
MONTH(transaction_date) AS months, YEAR(transaction_date) AS years, product_number AS productno,
SUM(qty) AS salesqty
FROM sales_events
WHERE product_number = #productID
-- including all transactions from last 24 full months until today
AND transaction_date >= CAST(YEAR(DATEADD( m , -23 , GETDATE())) AS CHAR(4)) + '-' + CAST(MONTH(DATEADD( m , -23 , GETDATE())) AS VARCHAR(2)) + '-01'
GROUP BY MONTH(transaction_date), YEAR(transaction_date), product_number
SELECT m.sorting, m.months, m.years, COALESCE(productno, 'No Sales') AS productno, COALESCE(kpl, 0) AS salesqty
FROM #strTemp_months m LEFT OUTER JOIN #strTemp_statistics s
ON m.months = s.months AND m.years = s.years
ORDER BY 1, 2 DESC
Related
I Want To Create A Stored procedure That return A Random Number Between (11111,99999)
Provided that the Number Should Not Exist In The Table
I use This complicated Function to Do that But I Need To Convert it to Stored Procedure
Function GiveRandomStudentNumber() As String
s:
Dim rnd As New Random
Dim st_num As String = rnd.Next(11111, 99999)
Dim cmd As New SqlCommand("select count(0) from student where st_num = " & st_num,con)
dd.con.Open()
Dim count As Integer = cmd.ExecuteScalar()
dd.con.Close()
If count <> 0 Then
GoTo s
Else
Return st_num
End If
End Function
this Function Is Works But I need To Convert it To Stored Procedure ..
Thanks In Advance ...
CREATE PROCEDURE [dbo].[Select_RandomNumber]
(
#Lower INT, --11111-- The lowest random number
#Upper INT --99999-- The highest random number
)
AS
BEGIN
IF NOT (#Lower < #Upper) RETURN -1
--TODO: If all the numbers between Lower and Upper are in the table,
--you should return from here
--RETURN -2
DECLARE #Random INT;
SELECT #Random = ROUND(((#Upper - #Lower -1) * RAND() + #Lower), 0)
WHILE EXISTS (SELECT * FROM YourTable WHERE randCol = #Random)
BEGIN
SELECT #Random = ROUND(((#Upper - #Lower -1) * RAND() + #Lower), 0)
END
SELECT #Random
END
Create a table of student IDs. Fill it up with IDs between X and Y. Every time you want to use an ID, remove it from the table.
create table [FreeIDs] (
[ID] int,
[order] uniqueidentifier not null default newid() primary key);
insert into [FreeIDs] ([ID]) values (11111),(11112),...,(99999);
to get a free ID:
with cte as (
select top(1) [ID]
from [FreeIDs]
order by [order])
delete cte
output deleted.ID;
The persisted predeterminer order speeds up generating new IDs.
BTW, if you're tempted to 'optimize' the table and go by a numbers table:
with Digits as (
select Digit
from (
values (0), (1), (2), (3), (4), (5),
(6), (7), (8), (9)) as t(Digit)),
Numbers as (
select u.Digit + t.Digit*10 +h.Digit*100 + m.Digit*1000+tm.Digit*10000 as Number
from Digits u
cross join Digits t
cross join Digits h
cross join Digits m
cross join Digits tm)
select top(1) Number
from Numbers
where Number between 11111 and 99999
and Number not in (
select ID
from Students)
order by (newid());
just don't. The requirement to randomize the set is a performance killer and the join to eliminate existing (used) IDs is also problematic. But most importantly the solution fails under concurrency, as multiple requests can get the same ID (and this increases as the number of free IDs is reduced). And of course, the semantically equivalent naive row-by-painfully-slow-row processing, like your original code or Kaf's answer, have exactly the same problem but are also just plain slow. It really worth testing the solution when all but one of the IDs are taken, watch the light dim as you wait for the random number generator to hit the jackpot...
I need to create an unordered list of dates for a news items archive... It should look like this
2011
Dec
Nov
etc..
2010
Dec
Nov
etc...
Older
Here is what I have so far..
Dim StartYear As DateFormat = Date.Now.Year
Dim EndYear As DateFormat = Date.Now.Year - 2
ltlArchives.Text = "<ul id=""ArchivesYears"">"
For StartYear = StartYear To EndYear Step -1
ltlArchives.Text = ltlArchives.Text + "<li>" + StartYear.ToString + "</li>"
Next
ltlArchives.Text = ltlArchives.Text + "<li>Other</li>"
ltlArchives.Text = ltlArchives.Text + "</ul>"
I can carry on with this adding in the for loops for months nested under each year however it doesn't seem very practical and will generate links for months even if there are no news item entries...
Is there a way I can build this tree automatically and only include the years/months that have news entries. I can pull a list of SQL Server timestamps from the DB for all the news items and then would like to populate the list based on that...
Can someone point me in the right direction?
You need to find out years and months with news item from SQL Server - say using distinct query. For example, assuming PostedDate column indicates entry date for new item
SELECT DISTINCT
datepart(year, n.PostedDate) as Year,
datepart(month, n.PostedDate) as Month,
FROM
dbo.NewsItem n
ORDER BY
Year desc, Month desc
This query will give you years and months that have news items. Now bind it with say repeater control to get what you want.
You could select out the distinct year, month values from the database and have the query order it for you.
SELECT DISTINCT DATEPART(YEAR, DateCreated), DATEPART(MONTH, DateCreated)
FROM [Blah]
ORDER BY 1 DESC, 2 DESC
When you loop thru the result remember the "previous year" in a variable and only display the year if it has changed (i.e. previousYear doesn't equal the row you are looking at)
Some simple code using a fictional YearMonth class just for demonstration
dim ltlArchives as string
ltlArchives = "<ul id=""ArchivesYears"">"
dim prevYear as Int32 = 0
For Each month as YearMonth in months
If (prevYear <> month.Year) Then
ltlArchives = ltlArchives + "<li>" + month.Year.ToString() + "</li>"
End If
ltlArchives = ltlArchives + "<li>" + month.Month.ToString() + "</li>"
prevYear = month.Year
Next
ltlArchives = ltlArchives + "</ul>"
Hi' I'm working with an asp.net application wich related to the registration of students, the data related to the students is inserted in to a database, each time a student is registered the field idStudent is incremented by one, what I want to do is when a new year begins reset the student value in order to begin from 1 in the new year.
idStudent Year 1 2011 2 2011 3
2011 4 2011 5 2011 ..... 1 2012 2
2012 3 2012 .......
How can I do this?
The databse is in sql server 2008
Hope your help
If I had this exact business requirement and I couldn't negotiate a better, more efficient way then this would be my approach:
Instead of using an artificial key (i.e. an identity column) I would utilize a composite key. To find out what your best bet for a composite key would be, you need to know the business rules and logic. In other words, a question you would have to ask is is the combination of year and id unique? In other words, per year a student id can only be used once...?
This is one of those times were you would benefit from a natural composite key.
You could add a computed column which derives the number based on the year. You just need to run the script below at the start of the new year. Do note that when a transaction is rolled back there will be gaps in the identity column. (and inserts in a previous year will create big gaps as well.)
-- test table & inserts
create table Students (Id int identity, year int, name varchar(20), StudentId int null)
insert into Students (year, name) values (2010, 'student1'), (2011, 'student1'), (2011, 'student2')
-- run this every year
EXEC ('ALTER TABLE Students DROP COLUMN StudentId ')
declare #count int, #sql varchar(max), #year int
declare c cursor local read_only
for select year + 1, max(Id) from Students group by year
open c
fetch next from c into #year, #count
select #sql = 'when year = ' + convert(varchar(10), #year - 1) + ' then Id '+ CHAR(13) + CHAR(10)
while ##FETCH_STATUS = 0
begin
select #sql = #sql + 'when year = ' + convert(varchar(10), #year) + ' then Id - ' + convert(varchar(10), #count) + CHAR(13) + CHAR(10)
fetch next from c into #year, #count
end
close c
deallocate c
select #sql = 'CASE ' + CHAR(13) + CHAR(10) + #sql + ' ELSE 0 END'
select #sql = 'ALTER TABLE Students ADD StudentId AS ' + isnull(#sql, 'Id') + ' PERSISTED'
exec (#sql)
I am retrieving two or more tables data based on conditions, so I am writing cursors in SQL server2005 like this:
create PROC [dbo].[uspCustomerInvoiceGetlist]
#CustomerID varchar(20)
AS
BEGIN
SELECT DeleteStatus,InvoiceID,CONVERT(varchar(16),CreationDate,101) + ' ' +
CONVERT(varchar(16),CreationDate,8) as CreationDate,AccountNumber,Sum(Amount) as Amount from tblInvoiceDetails where CustomerID=#CustomerID Group By InvoiceID,CreationDate,AccountNumber,DeleteStatus
END
alter procedure uspCustomerInvoiceGetlist1(#CustomerID varchar(50)) as
Begin
Declare #InvoiceID int, #Balance float
Declare MyCur Cursor for select InvoiceID from tblInvoiceHeader where CustomerID=#CustomerID
Open MyCur
Fetch next from mycur into #InvoiceID
While ##Fetch_Status=0
Begin
set #Balance=(select sum(Balance) from tblInvoicePaymentDetails where CustomerId=#CustomerID and InvoiceId=#InvoiceID)
print #Balance
if #Balance!=0
SELECT h.DeleteStatus,h.InvoiceID,CONVERT(varchar(16),h.CreationDate,101) + ' ' +
CONVERT(varchar(16),h.CreationDate,8) as CreationDate,h.AccountNumber,Sum(h.Amount) as Amount, #Balance as Balance from tblInvoiceDetails h inner join tblInvoicePaymentDetails p on h.CustomerID=p.CustomerID and h.InvoiceID=#InvoiceID
Group By h.InvoiceID,h.CreationDate,h.AccountNumber,h.DeleteStatus
else
SELECT DeleteStatus,InvoiceID,CONVERT(varchar(16),CreationDate,101) + ' ' +
CONVERT(varchar(16),CreationDate,8) as CreationDate,AccountNumber,Sum(Amount) as Amount, 0 as Balance from tblInvoiceDetails where CustomerID=#CustomerID and InvoiceID=#InvoiceID Group By InvoiceID,CreationDate,AccountNumber,DeleteStatus
Fetch Next From MyCUr into #InvoiceID
End
Close Mycur
Deallocate Mycur
End
output is correct like that (two outputs based on condition two are more)
Status InvoiceID Date account no Amount Balance
1 1009 06/21/2011 10:22:15 10009 450 350
Status InvoiceID Date account no Amount Balance
1 1010 06/21/2011 10:22:33 100000 690 0
But I can bind the all values (both) in grid view in ASP.NET 2.0. How is it possible?
In the Front End, You will get Two DataTable.
You just Combine those two DataTable into a Single One.
And Bind the DataTable with the GridView.
Merge like this:
dt1.Merge(dt2); //dt1 is the DataTable 1 and dt2 is the DataTable 2
Get the Set of Tables as DataSet and Merge using For Loop: I have given code below:
DataTable dtmerge = new DataTable();
for (int i = 0; i < ds.Tables.Count; i++) // ds is the DataSet
{
dtmerge.Merge(ds.Tables[i]);
}
SELECT T.GLTR_COMP_CODE,
T.GLTR_ACCT_CODE,
T.GLTR_DEPT_NO,
M.GLMA_ACCT_NAME ,
CAST(T.GLTR_PSTNG_TYPE AS VARCHAR) + CAST(T.GLTR_PSTNG_NO AS VARCHAR) REF_NO,
CAST(T.GLTR_DOC_CODE AS VARCHAR) + CAST(T.GLTR_OUR_DOC_NO AS VARCHAR) DOC_NO,
T.GLTR_DOC_DATETIME DOC_DATE,
T.GLTR_DOC_NARR NARRATIVE,
T.GLTR_PSTNG_DATETIME,
CASE SIGN(T.GLTR_TRAN_AMT) WHEN + 1 THEN T.GLTR_TRAN_AMT ELSE 0 END DEBIT,
CASE SIGN(T.GLTR_TRAN_AMT) WHEN - 1 THEN T.GLTR_TRAN_AMT ELSE 0 END CREDIT,
T.GLTR_TRAN_AMT AMOUNT,
T.GLTR_FC_CODE FC_CODE,
T.GLTR_FC_AMT FC_AMOUNT
FROM GLAS_GL_TRANSACTIONS T
LEFT OUTER JOIN GLAS_GL_MASTERS M ON M.GLMA_COMP_CODE = T.GLTR_COMP_CODE
AND M.GLMA_ACCT_CODE = T.GLTR_ACCT_CODE
AND M.GLMA_DEPT_NO = T.GLTR_DEPT_NO
WHERE T.GLTR_PSTNG_DATETIME BETWEEN COALESCE(#DATE_FROM, T.GLTR_PSTNG_DATETIME)
AND COALESCE(#DATE_TO, T.GLTR_PSTNG_DATETIME)
AND T.GLTR_COMP_CODE =#COMP_CODE
AND M.GLMA_YEAR = CONVERT(NUMERIC(8, 2), DATEPART(YYYY, #DATE_FROM))
AND M.GLMA_ACCT_CODE BETWEEN COALESCE(#ACCT_CODE_FROM, M.GLMA_ACCT_CODE)
AND COALESCE(#ACCT_CODE_TO, M.GLMA_ACCT_CODE)
ORDER BY T.GLTR_ACCT_CODE, T.GLTR_PSTNG_DATETIME, T.GLTR_DOC_CODE, T.GLTR_OUR_DOC_NO
This is my query in dataset...how can i view this in crystal report ......some of fields above have concatenate how can i display?
As long as you have created the dataset in your report you should be able to add the field to the report and view it.
The one thing I notice is that you do not have alias on your concatenated fields which may cause problems when designing your report.