Search by parameter only if user enters a value - asp.net

I want to rewrite this query so that if #UserName gets passed in a null value then it leaves the Client_User out of the search criteria. I only want it to search by name if the user enters a name in the username textbox on the webform. I'm not sure how to do this.
select * from weblogs.dbo.vwlogs
where Log_time between #BeginDate and #EndDAte
and client_user=#UserName

select * from weblogs.dbo.vwlogs where Log_time between #BeginDate and #EndDAte
and
(#UserName IS NULL OR client_user=#UserName)

select *
from weblogs.dbo.vwlogs
where Log_time between #BeginDate and #EndDAte
and (client_user=#UserName or #UserName IS null)

The solution from Kristen will work, but if you need performance, don't do it because the plan will be cached only for the first condition.
So, if your procedure gets called with the NULL parameter first, that query will be cached.
If you need higher performance, use an IF statement and create two distinct queries.
In more complicated queries event sp_execsql will be faster.

The best solution is to utilize sp_execute_sql. For example:
--BEGIN SQL
declare #sql nvarchar(4000)
set #sql =
'select * from weblogs.dbo.vwlogs
where Log_time between #BeginDate and #EndDate'
+ case when #UserName is null then '' else 'and client_user = #UserName' end
sp_execute_sql
#sql
, #params = '#UserName varchar(50)'
, #UserName = #UserName
--END SQL
As muerte mentioned, this will have a performance benefit. According to BOL:
sp_executesql can be used instead of stored procedures to execute a Transact-SQL statement a number of times when the change in parameter values to the statement is the only variation. Because the Transact-SQL statement itself remains constant and only the parameter values change, the Microsoft® SQL Server™ query optimizer is likely to reuse the execution plan it generates for the first execution.

Related

Run table names pulled from sys.tables SQL Server 2008R2

I need to identify tables that were created today by an interface, which I was able to do by using following query:
Note: The interface changes table names on daily basis.
SELECT [name] AS [TableName]
FROM sys.tables
WHERE NAME LIKE '_XYZExport_%'
AND CAST(create_date AS DATE) = CAST(GETDATE() AS DATE)
ORDER BY NAME
What I need:
Once the table names are pulled, I need dump its data into a new table. How can this be done easily?
Example:
Following tables returned from my queries:
_XYZExport_B02
_XYZExport_B12
_XYZExport_B22
I want to take these returned tables and insert their data into an existing Archive table using Union All.
Any help would be great!
You are on the right track with your "cursor" tag. I would recommend creating an insert statement and executing it each cursor loop.
DECLARE #TableName sysname
DECLARE #SQLInsert VARCHAR(100)
DECLARE TableNamesCursor CURSOR FAST_FORWARD READ_ONLY FOR
SELECT [name] AS [TableName]
FROM sys.tables
WHERE NAME LIKE '_XYZExport_%'
AND CAST(create_date AS DATE) = CAST(GETDATE() AS DATE)
ORDER BY NAME
OPEN TableNamesCursor
FETCH NEXT FROM TableNamesCursor INTO #TableName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #SQLInsert = 'INSERT INTO ArchiveTable SELECT * FROM ' + #TableName
EXEC sp_executesql #SQLInsert
FETCH NEXT FROM TableNamesCursor INTO #TableName
END
CLOSE TableNamesCursor
DEALLOCATE TableNamesCursor
Hope that gets you going.
Noel

SQL Server triggers aren't working with Linq to SQL on ASP.NET

My work colleague is making the ASP.NET Web Forms application collecting data. I'm administrating SQL Server database of it. Based on databse he makes objects to Web Forms using Linq to SQL. He wanted me to make recodrds in Osoby to change dataDodania with date of generation the object and dataModyfikacji with date of last update. Having experience in PL/SQL I made simple triggers for this. The problem is that triggers work when I run SQL statements in SQL Server Management Studio 2008 nicely, but when used in application - they are omitted, not making changes needed. Here is triggers SQL code:
CREATE TRIGGER [dbo].[DodanieOsoby]
ON [dbo].[Osoby]
INSTEAD OF INSERT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
INSERT INTO Osoby(dataDodania, dataModyfikacji, loginId, rola, imie, imieDrugie, nazwisko, plec, wiek,pESEL,wyksztalcenie,opieka,ulica, nrDom, nrLokal, miejscowosc, obszar, kodPoczty, telefonKontakt, telefonStacjo, email, zatrudnienie, stanowisko, przedsiebiorstwo)
SELECT GETDATE(), GETDATE(), loginId, rola, imie, imieDrugie, nazwisko, plec, wiek, pESEL, wyksztalcenie,opieka,ulica, nrDom, nrLokal, miejscowosc, obszar, kodPoczty, telefonKontakt, telefonStacjo, email, zatrudnienie, stanowisko, przedsiebiorstwo
FROM inserted
END
And for UPDATE of Osoby...
CREATE TRIGGER [dbo].[AktualizacjaOsoby]
ON [dbo].[Osoby]
AFTER UPDATE
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
UPDATE Osoby
SET dataModyfikacji = GETDATE()
WHERE id in
(SELECT DISTINCT id from Inserted)
END
Possible this be helpful for you (if dbo.Osoby is view) -
ALTER TRIGGER dbo.trg_IOIU_vw_WorkOut
ON dbo.vw_WorkOut
INSTEAD OF INSERT, UPDATE
AS BEGIN
SET NOCOUNT ON
SET XACT_ABORT ON
DECLARE
#WorkOutID BIGINT
, #DateOut DATETIME
, #EmployeeID INT
DECLARE workout CURSOR LOCAL READ_ONLY FAST_FORWARD FOR
SELECT
WorkOutID
, DateOut
, EmployeeID
FROM INSERTED
OPEN workout
FETCH NEXT FROM workout INTO
#WorkOutID
, #DateOut
, #EmployeeID
WHILE ##FETCH_STATUS = 0 BEGIN
IF NOT EXISTS(
SELECT 1
FROM dbo.WorkOut
WHERE WorkOutID = #WorkOutID
)
BEGIN
INSERT INTO dbo.WorkOut
(
EmployeeID
, DateOut
)
SELECT
#EmployeeID
, #DateOut
SELECT SCOPE_IDENTITY() -- if you use LINQ need return new ID to client
END
ELSE BEGIN
UPDATE dbo.WorkOut
SET
EmployeeID = #EmployeeID
, DateOut = #DateOut
WHERE WorkOutID = #WorkOutID
END
FETCH NEXT FROM workout INTO
#WorkOutID
, #DateOut
, #EmployeeID
END
CLOSE workout
DEALLOCATE workout
END

how to get username into sql trigger when multiple users signed on from asp membership

I am trying to log all the changes to database but was unable to find a way to get current username to the trigger. I have triggerData table which stores the information of user
(guid (userid), data (username), logintime) these are inserted when user sign in.
Here is the trigger
declare #UserIsOnlineTimeWindow DateTime
declare #currenttime DateTime
set #currenttime = GETDATE()
set #UserIsOnlineTimeWindow = 10
DECLARE #uname nvarchar(max)
**set #uname = (SELECT T.UserName from (SELECT distinct u.UserName FROM aspnet_Users u
WHERE IsAnonymous = 'FALSE' AND ((#currenttime - #UserIsOnlineTimeWindow) < u.LastActivityDate)) as t,
TriggerData td, aspnet_Users au
WHERE t.UserName = td.Data and td.guid = au.UserId and td.logintime = (select MAX(td.logintime) from TriggerData td))**
DECLARE #ComputerName nvarchar(50)
DECLARE #IPAddr nvarchar(50)
DECLARE #BroadcastIP nvarchar(50)
DECLARE #auditInsert nvarchar(255)
SET #ComputerName = (SELECT ComputerName FROM inserted)
SET #IPAddr = (SELECT ISNULL(IPAddr,0) FROM inserted)
SET #BroadcastIP = (SELECT ISNULL(BroadcastIP,0) FROM inserted)
SET #auditInsert = #ComputerName+' '+#IPAddr+' '+#BroadcastIP
Begin
INSERT INTO Audit(OldInfo ,NewInfo,[User],Date1,Type,TableName) VALUES('New Record',#auditInsert, #uname ,GETDATE(),'Added','LabIP')
End
The query for the username does not give the currently modifying user instead it gives the recently logged in user. Any help is appreciated. Thanks!
Like marc_s said triggers can and will be applied once per batch. In your case however it sounds like your current application is only inserting once per batch so it's working fine. Your will have a problem however if anyone tries to insert more than one row. Say from another part of the application, or from a query window. It's really up to you if you want to fix it now or wait for it to break (if ever).
I've re-formated the query that is giving you problems.
set #uname = (SELECT T.UserName FROM
(SELECT DISTINCT u.UserName
FROM aspnet_Users u
WHERE IsAnonymous = 'FALSE'
AND ((#currenttime - #UserIsOnlineTimeWindow) < u.LastActivityDate)) AS t
JOIN TriggerData td
ON t.UserName = td.Data
JOIN aspnet_Users au
ON td.guid = au.UserId
WHERE td.logintime = (select MAX(td.logintime) from TriggerData td))
You can see from this where your problem is. If you look in the WHERE clause you will see that you are specifically pulling the row that last logged in. If you are not using an application id (although it sounds like you are) you can use USER_NAME() or SUSER_SNAME() to pull the current user. Unfortunatly based on the information you have provided I don't think you will be able to pull the user that actually made the change, again assuming that you are using an application id.
You might try putting the SPID (id for the current connection) into your users table. You can retrieve this as ##SPID. That way while you are in the current connection you will always be able to tell who the current user is. Of course this will only work if you are holding onto the connection for the whole time the user is logged in (probably not in a web based application).
Last (and unfortunatly least) is rather than using triggers create stored procedures to do your inserts. Pass in the current user into the SP as part of the parameter list and do your logging there.
Sorry I couldn't help more.

Calling a stored procedure that takes 2 parameters from inside a stored procedure that takes 1 parameter

I have a stored procedure usp_gethomedata(<current year>) that takes only current year (2012) as parameter and displays data for 01-01-2012 to current date of 2012 i.e 26-11-2012.
I have another stored procedure that shows similar data in usp_gethomedata(<start date>, <end date>) that takes 2 parameter start and end date ranges from 01-01-1900 to 26-11-2012.
Now, I am not sure how to call the second stored procedure from inside the 1st one so that it display me data for 01-01-2012 to 26-11-2012 provided I have to keep in mind.. that while calling the 1st procedure I can supply only the 2012 i.e the current year as a parameter from my asp .net application.
Please help.
No you cannot create overloaded stored procedure. One will override the other. Though Overloaded function is possible. So, change the inner stored procedure to some different name or the vice versa (i.e. outer stored procedure). Here is an example that will simulate your situation.
-- Outer stored procedure
USE [Test]
GO
-- exec [dbo].[USP_GetHomeData] 2012
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[USP_GetHomeData]') AND type in (N'P', N'PC'))
DROP PROCEDURE [dbo].[USP_GetHomeData]
GO
CREATE PROCEDURE [dbo].[USP_GetHomeData]
-- Add the parameters for the stored procedure here
(
#Year INT
)
AS
BEGIN
DECLARE #startDate DATETIME='1/1/' + CAST(#Year AS VARCHAR(4)) -- mm/dd/yyyy
DECLARE #endDate DATETIME=GETDATE() -- mm/dd/yyyy
SELECT [Date] = DATEADD(Day,Number,#startDate)
FROM master..spt_values WITH(NOLOCK)
WHERE Type='P'
AND DATEADD(day,Number,#startDate) <= #endDate
exec [dbo].[USP_GetHomeData_Inner] '1/1/1900', #endDate
END
-- Inner stored procedure
USE [Test]
GO
-- exec [dbo].[USP_GetHomeData_Inner] '1/1/1900', '11/25/2012'
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[USP_GetHomeData_Inner]') AND type in (N'P', N'PC'))
DROP PROCEDURE [dbo].[USP_GetHomeData_Inner]
GO
CREATE PROCEDURE [dbo].[USP_GetHomeData_Inner]
-- Add the parameters for the stored procedure here
(
#startDate DATETIME
,#endDate DATETIME
)
AS
BEGIN
;WITH Calender AS
(
SELECT #startDate AS CalanderDate
UNION ALL
SELECT CalanderDate + 1 FROM Calender
WHERE CalanderDate + 1 <= #endDate
)
SELECT [Date] = CONVERT(VARCHAR(10),CalanderDate,25)
FROM Calender WITH(NOLOCK)
OPTION (MAXRECURSION 0)
END
The result (partial) is as under
Hope this helps.

Preferred Method to Catch Specific OleDB Error

Ok - I have a situation in which I must execute a dynamically built stored procedure against tables that may, or may not be in the database. The data retrieved is then shunted to a VB.Net backed ASP based report page. By design, if the tables are not present in the database, the relevant data is automatically hidden on the report page. Currently, I'm doing this by checking for the inevitable error, and hiding the div in the catch block. A bit kludgy, but it worked.
I can't include the VB code-behind, but the relevant stored procedure is included below.
However, a problem with this method was recently brought to my attention when, for no apparent reason, the div was being hidden even though the proper data was available. As it turned out, the user trying to select the table in the dynamic SQL call didn't have the proper select permissions, an easy enough fix once I could track it down.
So, two fold question. First and foremost - is there a better way to check for a missing table than through catching the error in the VB.Net codebehind? All things considered, I'd rather save the error checking for an actual error. Secondly, is there a preferred method to squirrel out a particular OLE DB error out of the general object caught by the try->catch block other than just checking the actual stack trace string?
SQL Query - The main gist of the code is that, due to the design of the database, I have to determine the name of the actual table being targeted manually. The database records jobs in a single table, but each job also gets its own table for processing data on the items processed in that job, and it's data from those tables I have to retrieve. Absolutely nothing I can do about this setup, unfortunately.
DECLARE #sql NVarChar(Max),
#params NVarChar(Max),
#where NVarChar(Max)
-- Retained for live testing of stored procedure.
-- DECLARE #Table NvarChar(255) SET #Table = N'tblMSGExportMessage_10000'
-- DECLARE #AcctID Integer SET #AcctID = 10000
-- DECLARE #Type Integer SET #Type = 0 -- 0 = Errors only, 1 = All Messages
-- DECLARE #Count Integer
-- Sets our parameters for our two dynamic SQL calls.
SELECT #params = N'#MsgExportAccount INT, #cnt INT OUTPUT'
-- Sets our where clause dependent upon whether we want all results or just errors.
IF #Type = 0
BEGIN
SELECT #where =
N' AND ( mem.[MSGExportStatus_OPT_CD] IN ( 11100, 11102 ) ' +
N' OR mem.[IngestionStatus_OPT_CD] IN ( 11800, 11802, 11803 ) ' +
N' OR mem.[ShortcutStatus_OPT_CD] IN ( 11500, 11502 ) ) '
END
ELSE
BEGIN
SELECT #where = N' '
END
-- Retrieves a count of messages.
SELECT #sql =
N'SELECT #cnt = Count( * ) FROM dbo.' + QuoteName( #Table ) + N' AS mem ' +
N'WHERE mem.[MSGExportAccount_ID] = #MsgExportAccount ' + #where
EXEC sp_executesql #sql, #params, #AcctID, #cnt = #Count OUTPUT
To avoid an error you could query the sysobjects table to find out if the table exists. Here's the SQL (replace YourTableNameHere). If it returns > 0 then the table exists. Create a stores procedure on the server that runs this query.
select count(*)
from sysobjects a with(nolock)
where a.xtype = 'U'
and a.name = 'YourTableNameHere'

Resources