TSQL Hack needed for getting a filter for data - tsql

A UI (before the report shows) shows a look up (Combo) that has
(ID = 0).All Organization Units
(ID =4).HR
(ID = 5).DEV
I need to:
Be able to show data of (4) + (5) if
(0) is selected.
Only (4) OR (5) if either HR or DEV is selected.
Lookup combo code (Selected Feeds the parameter in the below query.)
Select 0 AS ID,'All Org' AS Name from DP_ORG_OrganizationUnit
where DP_ORG_OrganizationUnit.Code IN {AccessData}
Union
SELECT
DP_ORG_OrganizationUnit.ID,
DP_ORG_OrganizationUnit.Name
FROM DP_ORG_OrganizationUnit where DP_ORG_OrganizationUnit.Code IN ('HR','DEV')
Report data row query
SET CONCAT_NULL_YIELDS_NULL OFF
DECLARE #EmpID as int;
DECLARE #OrganizationUnit as int;
DECLARE #StartDate as datetime;
DECLARE #EndDate as datetime;
SET #EmpID = ?;
SET #StartDate = ?;
SET #EndDate = ?;
SET #OrganizationUnit = ?;
SELECT
Employee.Code,
Employee.Name1+' '+Employee.Name2+' '+Employee.Name3+' '+Employee.Name4+' '+Employee.Name5 AS FullName,
Employee.OrganizationUnit,
ContractType.Name,
EmployeeContract.StartDate,
EmployeeContract.EndDate
FROM Employee INNER JOIN (ContractType INNER JOIN EmployeeContract
ON ContractType.ID = EmployeeContract.ContractType)
ON Employee.ID = EmployeeContract.Employee
WHERE (Employee.ID = #EmpID OR #EmpID=0)
AND
(Employee.OrganizationUnit = #OrganizationUnit OR #OrganizationUnit=0)
AND NOT((EndDate < #StartDate or StartDate > #EndDate));
Any way I can achieve it from the looks of it? 0=0 would show all the data from other
departments too..
Anybody :-o?

First off, your lookup combo code could be tightened up a bit:
-- the FROM clause was superfluous
SELECT 0 AS ID,'All Org' AS Name
UNION ALL
-- the two-part identifiers were superfluous (only one table)
SELECT ID, Name
FROM DP_ORG_OrganizationUnit
WHERE Code IN ('HR','DEV')
For the report query, the simplest form would be:
WHERE
((#OrganizationUnit > 0 AND Employee.OrganizationUnit = #OrganizationUnit) OR
(#OrganizationUnit = 0 AND Employee.OrganizationUnit IN (4,5)))

something like this should work
Where (Employee.OrganizationUnit = case when #OrganizationUnit=0 then 4 else #OrganizationUnit end OR case when #OrganizationUnit=0 then 5 else #OrganizationUnit end)

Try this, which should use indexes on your query...
DECALRE #FilterValues (FilterValue int not null primary key)
IF #Param=0
BEGIN
INSERT INTO #FilterValues VALUES (4)
INSERT INTO #FilterValues VALUES (5)
END
ELSE ID #PAram IS NOT NULL
BEGIN
INSERT INTO #FilterValues VALUES (#Param)
END
SELECT
....
FROM YourTable y
INNER JOIN #FilterValues f ON y.Value=f.Value
WHERE .....

KM's version will work, but this query does not need a temp table...
SELECT *
FROM Employee
WHERE (
#OrganizationUnit = 0
OR
(
#OrganizationUnit <> 0
AND
Employee.OrganizationUnit = #OrganizationUnit
)
)

How about
WHERE (Employee.ID = #EmpID OR #EmpID=0)
AND
(Employee.OrganizationUnit BETWEEN ISNULL(NULLIF(#OrganizationUnit,0),0) AND ISNULL(NULLIF(#OrganizationUnit,0),99))
AND NOT((EndDate < #StartDate or StartDate > #EndDate));

Related

Can set the value of the previous query

how to SET return value to new query ?
query like :
DECLARE #count;
SELECT COUNT(*) FROM a WHERE x = 1;
SET #count = //result_last_query
SELECT * FROM b WHERE z = #count;
As simple as :
SELECT * FROM b
where z=(select count(*) FROM a FROM a WHERE x = 1;)}
You dont even require any variable declaration
thanks good solution
I modified the code of answers
SELECT *
FROM B
WHERE id IN (
SELECT COUNT(*) AS count
FROM A
WHERE x = 1
)
If you want get the resultset of the select query and want to access the row count in next statement you could use ##rowcount
Example Usage:
Select top 10 * from SomeTable
SELECT ##rowcount
First query in above script will return 10 rows and the second query will return 10.
You are almost done. You can set in query as the below:
DECLARE #count INT
SELECT #count = COUNT(*) FROM a WHERE x = 1
SELECT * FROM b WHERE z = #count
All you need to do is to put the variable in the query itself, in order to set the variable's value with the row count.
Then you can use the variable's value in the next query.
DECLARE #count;
SELECT #count = COUNT(*) FROM a WHERE x = 1;
SELECT * FROM b WHERE z = #count;
But, be careful that the variable will only be visible during execution so you need to execute this entire script all at once in order for the #count variable to work for the last query.

How to filter records for all rows?

I am designing a SQL query to extract all records from a given table. But the trick here is that this logic is based on a numeric database field. So there are 4 choices: 0,1,2,3. If user selects 0,1, or 2, then my query returns rows with the specified value. But if they choose 3, it should return all of the rows. How do I do this in SQL? I know if this was a string, I could do something like:
WHERE = CASE WHEN = 3 THEN '%' ELSE END
But in this case, is an integer. Sounds relatively simple but I'm getting errors.
Try this:
SELECT *
FROM <YOUR_TABLE>
WHERE
(
<YOUR_COLUMN> = #InputValue OR
3 = #InputValue
)
Where #InputValue is the name of parameter sent to the query.
The simplest way is to do this:
select MyColumn
from MyTable
where ( MyValue = #MyParameter or #MyParameter = 3)
If your interested in better optimization, then you can do this, but it is less maintainable:
if (#MyParameter = 3)
select MyColumn
from MyTable
else
select MyColumn
from MyTable
where MyValue = #MyParameter
If I were forced to implement this functionality, then I would probably do this, just to make things clear:
declare #AllRecords nchar(1)
if (#MyParameter = 3)
set #AllRecords = N'Y'
else
set #AllRecords = N'N'
select MyColumn
from MyTable
where (MyValue = #MyParameter or #AllRecords = N'Y')
Hopefully, I won't ever have to implement a system that mixes flags and data value in this way.
UPDATED
Here is a version that should work with your expanded requirements (this requires one of the newer versions of SQL Server, I think):
declare #SelectedLevels table (LevelId int not null primary key)
if #LevelId = 3
insert into #SelectedLevels (LevelId) values (1), (2)
else if #LevelId = 5
insert into #SelectedLevels (LevelId) values (0), (1), (2)
else
insert into #SelectedLevels (LevelId) values (#LevelId)
select mt.MyColumn
from MyTable mt
inner join #SelectedLevels sl on sl.LevelId = MyTable.LevelId
if #Param = 3
begin
select *
from #T
end
else
if #Param = 2
begin
select *
from #T
where id in (0,1)
end
else
begin
select *
from #T
where id = #Param
end

Establishing Upper / Lower Bound in T-SQL Procedure

I am trying to establish upper / lower bound in my stored procedure
below and am having some problems at the end (I am getting no results
where, without the temp table inner join i get the expected results).
I need some help where I am trying to join the columns in my temp table #PageIndexForUsers
to the rest of my join statement and I am mucking something up with
this statement:
INNER JOIN
#PageIndexForUsers ON ( dbo.aspnet_Users.UserId =
#PageIndexForUsers.UserId AND #PageIndexForUsers.IndexId >= #PageLowerBound AND
#PageIndexForUsers.IndexId <= #PageUpperBound )
I could use feedback at this point - and, any advice on how to improve
my procedure's logic (if you see anything else that needs improvement) is also appreciated.
Thanks in advance...
ALTER PROCEDURE dbo.wb_Membership_GetAllUsers
#ApplicationName nvarchar(256),
#sortOrderId smallint = 0,
#PageIndex int,
#PageSize int
AS
BEGIN
DECLARE #ApplicationId uniqueidentifier
SELECT #ApplicationId = NULL
SELECT #ApplicationId = ApplicationId FROM dbo.aspnet_Applications WHERE LOWER(#ApplicationName) = LoweredApplicationName
IF (#ApplicationId IS NULL)
RETURN 0
-- Set the page bounds
DECLARE #PageLowerBound int
DECLARE #PageUpperBound int
DECLARE #TotalRecords int
SET #PageLowerBound = #PageSize * #PageIndex
SET #PageUpperBound = #PageSize - 1 + #PageLowerBound
BEGIN TRY
-- Create a temp table TO store the select results
CREATE TABLE #PageIndexForUsers
(
IndexId int IDENTITY (0, 1) NOT NULL,
UserId uniqueidentifier
)
-- Insert into our temp table
INSERT INTO #PageIndexForUsers (UserId)
SELECT u.UserId
FROM dbo.aspnet_Membership m, dbo.aspnet_Users u
WHERE u.ApplicationId = #ApplicationId AND u.UserId = m.UserId
ORDER BY u.UserName
SELECT #TotalRecords = ##ROWCOUNT
SELECT dbo.wb_Profiles.profileid, dbo.wb_ProfileData.firstname, dbo.wb_ProfileData.lastname, dbo.wb_Email.emailaddress, dbo.wb_Email.isconfirmed, dbo.wb_Email.emaildomain, dbo.wb_Address.streetname, dbo.wb_Address.cityorprovince, dbo.wb_Address.state, dbo.wb_Address.postalorzip, dbo.wb_Address.country, dbo.wb_ProfileAddress.addresstype,dbo.wb_ProfileData.birthday, dbo.wb_ProfileData.gender, dbo.wb_Session.sessionid, dbo.wb_Session.lastactivitydate, dbo.aspnet_Membership.userid, dbo.aspnet_Membership.password, dbo.aspnet_Membership.passwordquestion, dbo.aspnet_Membership.passwordanswer, dbo.aspnet_Membership.createdate
FROM dbo.wb_Profiles
INNER JOIN dbo.wb_ProfileAddress
ON
(
dbo.wb_Profiles.profileid = dbo.wb_ProfileAddress.profileid
AND dbo.wb_ProfileAddress.addresstype = 'home'
)
INNER JOIN dbo.wb_Address
ON dbo.wb_ProfileAddress.addressid = dbo.wb_Address.addressid
INNER JOIN dbo.wb_ProfileData
ON dbo.wb_Profiles.profileid = dbo.wb_ProfileData.profileid
INNER JOIN dbo.wb_Email
ON
(
dbo.wb_Profiles.profileid = dbo.wb_Email.profileid
AND dbo.wb_Email.isprimary = 1
)
INNER JOIN dbo.wb_Session
ON dbo.wb_Profiles.profileid = dbo.wb_Session.profileid
INNER JOIN
dbo.aspnet_Membership
ON dbo.wb_Profiles.userid = dbo.aspnet_Membership.userid
INNER JOIN
dbo.aspnet_Users
ON dbo.aspnet_Membership.UserId = dbo.aspnet_Users.UserId
INNER JOIN
dbo.aspnet_Applications
ON dbo.aspnet_Users.ApplicationId = dbo.aspnet_Applications.ApplicationId
INNER JOIN
#PageIndexForUsers ON ( dbo.aspnet_Users.UserId =
#PageIndexForUsers.UserId AND #PageIndexForUsers.IndexId >= #PageLowerBound AND
#PageIndexForUsers.IndexId <= #PageUpperBound )
ORDER BY CASE #sortOrderId
WHEN 1 THEN dbo.wb_ProfileData.lastname
WHEN 2 THEN dbo.wb_Profiles.username
WHEN 3 THEN dbo.wb_Address.postalorzip
WHEN 4 THEN dbo.wb_Address.state
END
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0 ROLLBACK TRAN
EXEC wb_ErrorHandler
RETURN 55555
END CATCH
RETURN #TotalRecords
END
GO
You don't have enough rows in #PageIndexForUsers, no?
If #PageSize = 50 and you want #PageIndex 2, then you are looking for rows 100 to 149 from #PageIndexForUsers. Do you have this many rows?
The row filter should be applied over the larger dataset that starts FROM dbo.wb_Profiles

Implementing and applying a string split in T-SQL

I have this statement in T-SQL.
SELECT Bay From TABLE where uid in (
select B_Numbers from Info_Step WHERE uid = 'number'
)
I am selecting "multiple" BAYs from TABLE where their uid is equal to a string of numbers like this:
B_Numbers = 1:45:34:98
Therefore, I should be selecting 4 different BAYs from TABLE. I basically need to split the string 1:45:34:98 up into 4 different numbers.
I'm thinking that Split() would work, but it doesn't and I get a syntax error.
Any thoughts from the T-SQL gods would be awesome!
Here is an implementation of a split function that returns the list of numbers as a table:
http://rbgupta.blogspot.com/2007/03/split-function-tsql.html
Looks like this would set you on your way...
Here is a method that uses an auxiliary numbers table to parse the input string. The logic can easily be added to a function that returns a table. That table can then be joined to lookup the correct rows.
Step 1: Create the Numbers table
SET NOCOUNT ON
GO
IF EXISTS
(
SELECT 1
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = 'Numbers'
AND TABLE_SCHEMA = 'dbo'
AND TABLE_TYPE = 'BASE TABLE'
)
BEGIN
DROP TABLE dbo.Numbers
END
GO
CREATE TABLE dbo.Numbers
(
Number smallint IDENTITY(1, 1) PRIMARY KEY
)
GO
WHILE 1 = 1
BEGIN
INSERT INTO dbo.Numbers DEFAULT VALUES
IF SCOPE_IDENTITY() = 32767
BEGIN
BREAK
END
END
GO
Step 2: Parse the Input String
CREATE FUNCTION dbo.ParseString(#input_string varchar(8000), #delim varchar(8000) = " ")
RETURNS TABLE
AS RETURN
(
SELECT Number
FROM dbo.Numbers
WHERE CHARINDEX
(
#delim + CONVERT(VARCHAR(12),Number) + #delim,
#delim + #input_string + #delim
) > 0
)
GO
**EXAMPLE**
SELECT * FROM dbo.ParseString('1:45:34:98',':')
Step 3: Use the results however you want/need
Number
------
1
34
45
98
End-To-End Example
Create function that returns the appropriate BNumber (of course change it to use the commented out SQL)
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION dbo.GetBNumber (#uid int)
RETURNS VARCHAR(8000)
AS
BEGIN
RETURN '1:45:34:98'
--select B_Numbers from Info_Step WHERE uid = #uid
END
GO
Use the use functions to return the desired results
-- Using Test Data
SELECT N.Number FROM Numbers N
JOIN dbo.ParseString(dbo.GetBNumber(12345),':') Q ON Q.Number = N.Number
-- Using Your Data (Untested but should work.)
SELECT N.Bay
FROM TABLE N
JOIN dbo.ParseString(dbo.GetBNumber(ENTER YOU NUMBER HERE),':') Q ON Q.Number = N.uid
Results
Number
------
1
34
45
98
You should keep your arrays as rows but if I understand your question I think this will work.
SELECT
Bay
From
TABLE
join Info_Step
on B_Numbers like '%'+ uid +'%'
where
Info_Step.uid = 'number'
This query will do a full table scan because of the like operator.
What you can do is loop through the B_Numbers entries and do your own split on : Insert those entries into a temp table and then perform your query.
DECLARE #i int
DECLARE #start int
DECLARE #B_Numbers nvarchar(20)
DECLARE #temp table (
number nvarchar(10)
)
-- SELECT B_Numbers FROM Info_Step WHERE uid = 'number'
SELECT #B_Numbers = '1:45:34:98'
SET #i = 0
SET #start = 0
-- Parse out characters delimited by ":";
-- Would make a nice user defined function.
WHILE #i < len(#B_Numbers)
BEGIN
IF substring(#B_Numbers, #i, 1) = ':'
BEGIN
INSERT INTO #temp
VALUES (substring(#B_Numbers, #start, #i - #start))
SET #start = #i + 1
END
SET #i = #i + 1
END
-- Insert last item
INSERT INTO #temp
VALUES (substring(#B_Numbers, #start, #i - #start + 1))
-- Do query with parsed values
SELECT Bay FROM TABLE WHERE uid in (SELECT * FROM #temp)
You can even try this
declare #str varchar(50)
set #str = '1:45:34:98'
;with numcte as(
select 1 as rn union all select rn+1 from numcte where rn<LEN(#str)),
getchars as(select
ROW_NUMBER() over(order by rn) slno,
rn,chars from numcte
cross apply(select SUBSTRING(#str,rn,1) chars)X where chars = ':')
select top 1
Bay1 = SUBSTRING(#str,0,(select rn from getchars where slno = 1))
,Bay2 = SUBSTRING(#str,
(select rn from getchars where slno = 1) + 1,
(((select rn from getchars where slno = 2)-
(select rn from getchars where slno = 1)
)-1))
,Bay3 = SUBSTRING(#str,
(select rn from getchars where slno = 2) + 1,
(((select rn from getchars where slno = 3)-
(select rn from getchars where slno = 2)
)-1))
,Bay4 = SUBSTRING(#str,
(select rn from getchars where slno = 3)+1,
LEN(#str))
from getchars
Output:
Bay1 Bay2 Bay3 Bay4
1 45 34 98

sql missing rows when grouped by DAY, MONTH, YEAR

If I select from a table group by the month, day, year,
it only returns rows with records and leaves out combinations without any records, making it appear at a glance that every day or month has activity, you have to look at the date column actively for gaps. How can I get a row for every day/month/year, even when no data is present, in T-SQL?
Create a calendar table and outer join on that table
My developer got back to me with this code, underscores converted to dashes because StackOverflow was mangling underscores -- no numbers table required. Our example is complicated a bit by a join to another table, but maybe the code example will help someone someday.
declare #career-fair-id int
select #career-fair-id = 125
create table #data ([date] datetime null, [cumulative] int null)
declare #event-date datetime, #current-process-date datetime, #day-count int
select #event-date = (select careerfairdate from tbl-career-fair where careerfairid = #career-fair-id)
select #current-process-date = dateadd(day, -90, #event-date)
while #event-date <> #current-process-date
begin
select #current-process-date = dateadd(day, 1, #current-process-date)
select #day-count = (select count(*) from tbl-career-fair-junction where attendanceregister <= #current-process-date and careerfairid = #career-fair-id)
if #current-process-date <= getdate()
insert into #data ([date], [cumulative]) values(#current-process-date, #day-count)
end
select * from #data
drop table #data
Look into using a numbers table. While it can be hackish, it's the best method I've come by to quickly query missing data, or show all dates, or anything where you want to examine values within a range, regardless of whether all values in that range are used.
Building on what SQLMenace said, you can use a CROSS JOIN to quickly populate the table or efficiently create it in memory.
http://www.sitepoint.com/forums/showthread.php?t=562806
The task calls for a complete set of dates to be left-joined onto your data, such as
DECLARE #StartInt int
DECLARE #Increment int
DECLARE #Iterations int
SET #StartInt = 0
SET #Increment = 1
SET #Iterations = 365
SELECT
tCompleteDateSet.[Date]
,AggregatedMeasure = SUM(ISNULL(t.Data, 0))
FROM
(
SELECT
[Date] = dateadd(dd,GeneratedInt, #StartDate)
FROM
[dbo].[tvfUtilGenerateIntegerList] (
#StartInt,
,#Increment,
,#Iterations
)
) tCompleteDateSet
LEFT JOIN tblData t
ON (t.[Date] = tCompleteDateSet.[Date])
GROUP BY
tCompleteDateSet.[Date]
where the table-valued function tvfUtilGenerateIntegerList is defined as
-- Example Inputs
-- DECLARE #StartInt int
-- DECLARE #Increment int
-- DECLARE #Iterations int
-- SET #StartInt = 56200
-- SET #Increment = 1
-- SET #Iterations = 400
-- DECLARE #tblResults TABLE
-- (
-- IterationId int identity(1,1),
-- GeneratedInt int
-- )
-- =============================================
-- Author: 6eorge Jetson
-- Create date: 11/22/3333
-- Description: Generates and returns the desired list of integers as a table
-- =============================================
CREATE FUNCTION [dbo].[tvfUtilGenerateIntegerList]
(
#StartInt int,
#Increment int,
#Iterations int
)
RETURNS
#tblResults TABLE
(
IterationId int identity(1,1),
GeneratedInt int
)
AS
BEGIN
DECLARE #counter int
SET #counter= 0
WHILE (#counter < #Iterations)
BEGIN
INSERT #tblResults(GeneratedInt) VALUES(#StartInt + #counter*#Increment)
SET #counter = #counter + 1
END
RETURN
END
--Debug
--SELECT * FROM #tblResults