Eliminating Duplicate Rows after Modifying Stored Procedure - sql-server-2008

Our Purchasing Department wants me to modify our application so that Requisition Requests can be searched using Part Numbers.
Each Requisition Request will contain at least one part, but could contain several parts.
The Stored Procedure is set up so that fields may be populated or they may not be, so it pulls data based on what is passed in.
Here is how the Stored Procedure works before my modifications:
ALTER PROCEDURE [dbo].[GetRequisitions](#status_id int, #startdate varchar(50), #enddate varchar(50),
#Vendor_Name varchar(200), #PO_Number varchar(50), #Req_ID int, #datesearch bit, #Part_ID varchar(50)) AS
BEGIN
SET NOCOUNT ON;
select
a.Req_ID, a.Vendor_ID, a.Vendor_Name, a.Req_Date, a.PO_Number, a.Requested_Buy, a.approved,
b.status,
c.supervisor_login,
case when d.req_id is not null then 1 else 0 end as HasDocs
from Req as a
join Status as b on (a.Status_ID=b.Status_ID)
left outer join Supervisor as c on (a.Requested_Login=c.Emp_Login)
left outer join req_doc as d on (a.Req_ID=d.req_id)
where
((#datesearch is null or #datesearch=0) or (a.Req_Date between #startdate and #enddate))
and ((#status_id is null or #status_id=0) or (a.Status_ID=#status_id))
and ((#Vendor_Name is null or LEN(RTrim(#Vendor_Name))=0) or (a.Vendor_Name like '%'+#Vendor_Name+'%'))
and ((#PO_Number is null or LEN(RTrim(#PO_Number))=0) or (a.PO_Number like #PO_Number+'%'))
and ((#Req_ID is null or #Req_ID=0) or (a.Req_ID=#Req_ID))
END
To query this same DataSet using an optional Part Number Part_ID field, I added the following code that joins with the Req_Part DataTable:
ALTER PROCEDURE [dbo].[GetRequisitions](#status_id int, #startdate varchar(50), #enddate varchar(50),
#Vendor_Name varchar(200), #PO_Number varchar(50), #Req_ID int, #datesearch bit, #Part_ID varchar(50)) AS
BEGIN
SET NOCOUNT ON;
select
a.Req_ID, a.Vendor_ID, a.Vendor_Name, a.Req_Date, a.PO_Number, a.Requested_Buy, a.approved,
b.status,
c.supervisor_login,
case when d.req_id is not null then 1 else 0 end as HasDocs
from Req as a
join Status as b on (a.Status_ID=b.Status_ID)
left outer join Supervisor as c on (a.Requested_Login=c.Emp_Login)
left outer join req_doc as d on (a.Req_ID=d.req_id)
join Req_Part e on (a.Req_ID=e.Req_ID)
where
((#datesearch is null or #datesearch=0) or (a.Req_Date between #startdate and #enddate))
and ((#status_id is null or #status_id=0) or (a.Status_ID=#status_id))
and ((#Vendor_Name is null or LEN(RTrim(#Vendor_Name))=0) or (a.Vendor_Name like '%'+#Vendor_Name+'%'))
and ((#PO_Number is null or LEN(RTrim(#PO_Number))=0) or (a.PO_Number like #PO_Number+'%'))
and ((#Req_ID is null or #Req_ID=0) or (a.Req_ID=#Req_ID))
and ((#Part_ID is null or LEN(RTrim(#Part_ID))=0) or (e.Part_ID like '%'+#Part_ID+'%'))
END
What is the best way to get this to stop returning the duplicate rows?
And, as a FYI: I did not design this database! I am aware of the fact that the column name Requested_Buy would be correctly spelled as Requested_By, but the database is already in production. Misspelled words really irritate me!

If I understood correctly, you may update query like this.
select
a.Req_ID, a.Vendor_ID, a.Vendor_Name, a.Req_Date, a.PO_Number, a.Requested_Buy, a.approved,
b.status,
c.supervisor_login,
case when d.req_id is not null then 1 else 0 end as HasDocs
from Req as a
join Status as b on (a.Status_ID=b.Status_ID)
left outer join Supervisor as c on (a.Requested_Login=c.Emp_Login)
left outer join req_doc as d on (a.Req_ID=d.req_id)
where
((#datesearch is null or #datesearch=0) or (a.Req_Date between #startdate and #enddate))
and ((#status_id is null or #status_id=0) or (a.Status_ID=#status_id))
and ((#Vendor_Name is null or LEN(RTrim(#Vendor_Name))=0) or (a.Vendor_Name like '%'+#Vendor_Name+'%'))
and ((#PO_Number is null or LEN(RTrim(#PO_Number))=0) or (a.PO_Number like #PO_Number+'%'))
and ((#Req_ID is null or #Req_ID=0) or (a.Req_ID=#Req_ID))
and ( ISNULL(#Part_ID,0) = 0
OR
EXISTS(SELECT NULL FROM Req_Part e WHERE a.Req_ID=e.Req_ID AND e.Part_ID like '%'+#Part_ID+'%')
)

Related

MySQL procedure returning wrong value (INSERT SELECT confronting)

I'm completely new to MySQL, and have been bumping with some errors, but always I do find solutions, except for this one I can't understand how to get around it.
The following MySQL Procedure returns me a value if variable "ue" is 1 or 0 (a bunch of exists validation). The validation part (SET ue = EXISTS...) works without the rest of the code, as it should, the problem is not there. But when I do execute the command INSERT INTO SELECT, it does not work, it always return 0 as response, when it should be 1. These two lines are getting in confrontation with each other.
INSERT INTO meetup_participation SELECT user_id, event_id FROM DUAL WHERE ue=1;
SELECT ue AS response;
The procedure should add 'user id' and 'event id' into meetup_participation, and then update the row at 'users' corresponding to the user with that 'user id' to increment the 'events participated'. And it also UPDATE to increment the participation in the event with this 'event id'.
I am using the SET ue to validate things like, if user exists, if event does exists, if date of event is still valid, and if user is not already in this table. So I am passing this value as a boolean to INSERT INTO meetup_participation [...] WHERE ue = 1. After that, I do SELECT ue to inform validation returned true and procedure executed without problems.
Here is the full procedure.
CREATE DEFINER=`user`#`localhost` PROCEDURE `join_event`(IN `user_id` BIGINT(64), IN `event_id` INT) NOT DETERMINISTIC MODIFIES SQL DATA SQL SECURITY DEFINER
begin
DECLARE ue INT;
SET ue = EXISTS(SELECT 1 FROM users WHERE fb_uid=user_id) AND EXISTS(SELECT 1 FROM meetup WHERE meet_id=event_id) AND EXISTS(SELECT 1 FROM meetup WHERE date > NOW() AND meet_id = event_id) AND EXISTS(SELECT 1 FROM meetup WHERE meet_id = event_id AND participants <= max_participants) AND NOT EXISTS(SELECT 1 FROM meetup_participation WHERE fb_uid = user_id AND meet_id = event_id);
INSERT INTO meetup_participation SELECT user_id, event_id FROM DUAL WHERE ue=1;
UPDATE users SET events_participated = events_participated + 1 WHERE fb_uid=user_id AND ue=1;
UPDATE meetup SET participants = participants + 1 WHERE meet_id=event_id AND ue=1;
SELECT ue AS response;
end
Thanks in advance.
The INSERT statement is executed separately from the SET ue =... statement. I'm not sure what you are trying to accomplish, but the code makes no sense.
If you want to add records to meetup_participation based on the EXISTS tests applied to each record in the users table, you would need to apply the tests to each record in your SELECT statement as part of the INSERT.
There are also numerous syntax/grammar issues in the code as shown.
If you could provide an explanation of what you are trying to accomplish with the procedure, that might allow someone to suggest the right way to code the procedure.
Selecting ue will not tell you if the procedure completed without error. You should research mysql transactions and mysql error handling. http://www.mysqltutorial.org/mysql-error-handling-in-stored-procedures/ is a good starting point.
You might end up with something like this
drop procedure if exists p;
delimiter //
CREATE DEFINER=`root`#`localhost` PROCEDURE `p`(
IN `inue` int,
IN `user_id` BIGINT(64),
IN `event_id` INT
)
LANGUAGE SQL
NOT DETERMINISTIC
MODIFIES SQL DATA
SQL SECURITY DEFINER
COMMENT ''
begin
DECLARE ue INT;
declare exit handler for sqlexception
begin
rollback;
insert into errors (msg) select concat('error ' ,inue,',',user_id,',',event_id);
end;
set autocommit = 0;
#set ue = inue;
SET ue = EXISTS(SELECT 1 FROM users WHERE fb_uid=user_id)
AND EXISTS(SELECT 1 FROM meetup WHERE meet_id=event_id)
#AND EXISTS(SELECT 1 FROM meetup WHERE dt > NOW() AND meet_id = event_id)
AND EXISTS(SELECT 1 FROM meetup WHERE meet_id = event_id AND ifnull(participants,0) <= max_participants)
AND NOT EXISTS(SELECT 1 FROM meetup_participation WHERE fb_uid = user_id AND meet_id = event_id)
;
select ue;
if ue = 1 then
start transaction;
INSERT INTO meetup_participation SELECT user_id, event_id,user_id, event_id;
UPDATE users SET events_participated = ifnull(events_participated,0) + 1 WHERE fb_uid=user_id = user_id;
UPDATE meetup SET participants = ifnull(participants,0) + 1 WHERE meet_id = event_id ;
commit;
end if;
SELECT ue AS response;
end //
The error table looks like this
CREATE TABLE `errors` (
`msg` varchar(2000) DEFAULT NULL,
`id` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1
Note I am not suggesting this is a solution appropriate to your site , you need to do the research and figure out what is best for you.

MySQL: join to SELECT statement issue

I've created the following stored procedure:
CREATE PROCEDURE `sp_DBR_Subbie_Cert_Input`(inPlot_ID varchar(25), inSubbie varchar(25), inCertDate date, inCertDetails varchar(50), inCertGross float ) BEGIN
SELECT inPlot_ID as 'Plot[Unit]', inSubbie as 'Subcontractor[inSub_ID]', inCertDate as 'Date[inC_Date]', inCertDetails as 'Details[inC_Det]', inCertGross as 'Gross[inC_Gross]', a.tblCert_Number as 'Prev. Cert#', a.tblCert_Gross as 'Prev Gross[inC_Prev]'
FROM hilmark.tblcertificates_j a
JOIN
(SELECT max(tblcertificates_j.tblCert_ID) as MaxCertID,
tblcertificates_j.tblCert_XID456 as MaxSubbie, tblcertificates_j.tblCert_XIDJob as MaxPlot
FROM hilmark.tblcertificates_j
GROUP BY tblcertificates_j.tblCert_XIDJob) x
WHERE x.MaxCertID=a.tblCert_ID AND x.MaxPlot=inPlot_ID AND x.MaxSubbie=inSubbie;
END $$
What I am trying to achieve is to allow a user to enter a new invoice for a supplier for a site. The query retrieves the last invoice for that supplier and that site. This works great except when this is the first invoice for that supplier on that site - nothing is returned. What I really need is a left outer join but in my code if I substitute the join with a left join I get a syntax error.
Where am I going wrong here?
I declared a variable called Max_CerT_ID and set it's value to the Cert_ID from the table or to zero if it's a completely new supplier. I then use this variable in case statements.
DROP PROCEDURE IF EXISTS sp_DBR_Subbie_Cert_Input
$$
CREATE PROCEDURE `sp_DBR_Subbie_Cert_Input`(inPlot_ID varchar(25), inSubbie varchar(25), inCertDate date, inCertDetails varchar(50), inCertGross float )
BEGIN
declare Max_Cert_Id int(11);
set Max_Cert_Id=0;
select max(y.tblCert_ID) into Max_Cert_ID from
hilmark.tblcertificates_j y
where y.tblCert_XIDJob=inPlot_ID and y.tblCert_XID456=inSubbie
group by y.tblCert_XIDJob, y.tblCert_XID456 ;
select 'dbr.colstyle',6,'%0.0F';
select 'dbr.colstyle',7,'%0.0F';
select 'dbr.colstyle',8,'%0.0F';
select inPlot_ID as 'Plot[Unit]',
inSubbie as 'Subcontractor[inSub_ID]',
inCertDate as 'Date[inC_Date]',
case
when Max_Cert_Id=0 then 1
else (select a.tblCert_Number from hilmark.tblcertificates_j a where a.tblCert_ID=Max_Cert_ID)+1
end as 'Cert#',
upper(inCertDetails) as 'Details[inC_Det]',
inCertGross as 'Gross[inC_Gross]',
case
when Max_Cert_Id=0 then 0
else (select a.tblCert_Gross from hilmark.tblcertificates_j a where a.tblCert_ID=Max_Cert_ID)
end as 'Prev. Gross',
case
when Max_Cert_Id=0 then inCertGross
else inCertGross-(select a.tblCert_Gross from hilmark.tblcertificates_j a where a.tblCert_ID=Max_Cert_ID)
end as 'Change in WIP';
END
$$
Try writing the query as:
SELECT inPlot_ID as `Plot[Unit]`, inSubbie as `Subcontractor[inSub_ID]`,
inCertDate as `Date[inC_Date]`, inCertDetails as `Details[inC_Det]`,
inCertGross as `Gross[inC_Gross]`, c.tblCert_Number as `Prev. Cert#`,
c.tblCert_Gross as `Prev Gross[inC_Prev]`
FROM hilmark.tblcertificates_j c LEFT JOIN
(SELECT max(c.tblCert_ID) as MaxCertID, c.tblCert_XID456 as MaxSubbie,
c.tblCert_XIDJob as MaxPlot
FROM hilmark.tblcertificates_j c
GROUP BY c.tblCert_XIDJob
) cmax
WHERE cmax.MaxCertID = c.tblCert_ID AND cmax.MaxPlot = c.inPlot_ID AND cmax.MaxSubbie = c.inSubbie;
Some notes:
I doubt this really does what you want, because MaxSubbie has an indeterminate value. But you save the query works. Why do you need both comparisons?
When using table aliases, make them abbreviations for the table so the query is easier to read.
Only use single quotes for string and date constants.

Stored Procedure Can't drop temp table it don't exist

SQL Server 2008
This is a continuation of my last question. Now I'm trying to create a stored procedure, however I can't execute it. When I execute it, an error message displays
"Cannot drop the table #MyReport", because it does not exist or you do not have permissions.
Please guide me in the right direction.
Below is my stored Procedure
Create PROCEDURE [dbo].[SEL_MyReport]
(
#employeeid int,
#date1 datetime,
#date2 datetime
)
AS
BEGIN
drop table #MyReport
Create Table #MyReport
(
employeeid int,
name varchar(30),
department varchar(30),
checkTime datetime
)
if (#employeeid > 0)
Begin
INSERT INTO #MyReport (employeeid,name, department, checkTime)
select emp.EmpolyeeID, emp.Name,dep.DeptName,tm.checkTime
from TimeInOut tm
left join Employee emp on emp.EmpolyeeId = tm.EmployeeId
left join Department dep on dep.DeptID = emp.defaultDeptID
where (DATEDIFF(s,#date1,tm.checktime) >=0
and DATEDIFF(s,#date2,tm.checktime)<=0) and emp.employeeID = #employeeid
SELECT
employeeid
,name
,department
,[Time In] = MIN(checkTime)
,[Time Out] = MAX(checkTime)
FROM #MyReport
GROUP BY employeeid,name, department, CAST(checktime AS DATE)
End
Else
Begin
INSERT INTO #MyReport (employeeid,name, department, checkTime)
select emp.EmpolyeeID, emp.Name,dep.DeptName,tm.checkTime
from TimeInOut tm
left join Employee emp on emp.EmpolyeeId = tm.EmployeeId
left join Department dep on dep.DeptID = emp.defaultDeptID
where (DATEDIFF(s,#date1,tm.checktime) >=0
and DATEDIFF(s,#date2,tm.checktime)<=0)
SELECT
employeeid
,name
,department
,[Time In] = MIN(checkTime)
,[Time Out] = MAX(checkTime)
FROM #MyReport
GROUP BY employeeid,name, department, CAST(checktime AS DATE)
End
END
Go
exec SEL_MyReport('639','05/01/2014','05/08/2014')
There is quite a bit I would change - here is the code.
You will notice
branching logic (if #employeeid > 0) has been replaced by a slightly more verbose WHERE clause
no need for #tables as far as I can tell, the SELECT should suffice
Unfortunately, I do not have anything to test against, but you should understand the general impression of it.
Further, your date filtering seemed quite strange, so I assumed you may have meant something else - I could be mistaken. Either way, the way the date filtering is now done is SARGable
CREATE PROCEDURE [dbo].[SEL_MyReport]
(
#employeeid INT,
#date1 DATETIME,
#date2 DATETIME
)
AS
BEGIN
SET NOCOUNT ON;
SELECT emp.EmpolyeeID
,emp.Name
,dep.DeptName
,[Time In] = MIN(tm.checkTime)
,[Time Out] = MAX(tm.checkTime)
FROM TimeInOut tm
LEFT
JOIN Employee emp on emp.EmpolyeeId = tm.EmployeeId
LEFT
JOIN Department dep on dep.DeptID = emp.defaultDeptID
WHERE tm.checktime >= #date1
AND tm.checktime <= #date2
/***********************************************************************************************************
* I've assumed what you may be trying to express, above
* You might also want to look at the BETWEEN() operator, remembering that it is inclusive in its behaviour
* (DATEDIFF(s,#date1,tm.checktime) >=0
* AND DATEDIFF(s,#date2,tm.checktime)<=0)
***********************************************************************************************************/
AND (emp.employeeID = #employeeid OR #employeeid <= 0)
GROUP BY emp.EmpolyeeID, emp.name, dep.department, CAST(tm.checktime AS DATE)
END
GO
Well, since the very first step of your Stored Procedure attempts to drop a table, this will obviously result in an error if that table does not exist.
To work around this, make sure you check whether the table exists, before you drop it:
IF OBJECT_ID('tempdb..#MyReport') IS NOT NULL DROP
TABLE #MyReport

SSRS String -vs- Data Integer

I am working on a SSRS report and using a parameter that allows you to choose multiple options. However, when I do this I get an error that states:
Error Converting Data Type nVarChar to Int.
The data in the database is an Integer. The parameter is set up as an Integer and it works great when only choosing one option. The issue comes when I choose multiple options.
My co-worker came up with one work-around but I would like something a little more elegant and easier to plug in if possible.
Here is his work-around:
ALTER PROCEDURE [dbo].[DtaPrep_MktgClients]
#BegDate date = NULL
, #EndDate date = NULL
, #Species varchar(50) = 'canine,feline,K9,'
 , #HospList varchar(500) = NULL
This is where the hospmastid string gets converted into a temp table
/*
--===================================--
HOSPITALS SETUP
--===================================--
*/
If #HospList IS NOT NULL
BEGIN
DECLARE #WorkHospList varchar(500)
SET #WorkHospList = #HospList
;
CREATE TABLE #HospList
( HospID smallint NULL )
SET #CommaLoc = charindex(',', #WorkHospList)
WHILE #CommaLoc > 1
BEGIN
SET #curVal = LEFT(#WorkHospList, #commaloc-1 )
INSERT INTO
#HospList( HospID )
SELECT #curVal
SET #WorkHospList = substring( #WorkHospList, #commaloc+1, len(#WorkHospList) )
SET #CommaLoc = charindex(',', #WorkHospList)
END
END
This is using the temp table to accomplish the same thing as a “WHERE Hospmastid IN (101,102,103)…”
Method 1
SELECT
HospitalMasterID
, ClientID
, FirstName
, LastName
FROM
Client
WHERE
HospitalMasterID IN (Select HospID From #HospList )
Needless to say, I am sure there is a better way to accomplish this. If anyone has any ideas, please let me know.
Here is the full Query I am now using. But it is not selecting anything so there is an issue with the Created Table.
USE [xxxxx]
GO
/****** Object: StoredProcedure [dbo].[PriceErosion] Script Date: 11/26/2013 8:26:33 AM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
/*
-- =============================================
-- Author:
-- Create date: 11/25/2013
-- Description: Determines the products in which the price was lowered and revenue lost during a set time period.
-- =============================================
*/
--#StartDate as Date = Null
--,#EndDate as Date = Null
--,#CurDate as Date = Null
--,#Hospital as VarChar = Null
--,#Division as Int = Null
Declare #StartDate as Date = Null
Declare #EndDate as Date = Null
Declare #Hospital as Int = Null
Declare #Division as Int = Null
DECLARE #curDate Date = Null
SET #curDate = GETDATE()
Set #StartDate = CASE WHEN #StartDate IS NULL THEN DATEADD(dd, -31, Dateadd(dd, -1, #curdate) ) ELSE #StartDate END
Set #EndDate = CASE WHEN #EndDate IS NULL THEN Dateadd(dd, -1, #curdate) ELSE #EndDate END
Set #Hospital = Case When #Hospital IS Null Then '3' Else #Hospital End;
IF OBJECT_ID('tempdb.dbo.#HospList') IS NOT NULL DROP TABLE #HospList ;
If #Hospital IS NOT NULL
BEGIN
DECLARE #WorkHospList varchar(500)
Declare #CommaLoc as Int
Declare #curVal as int
SET #WorkHospList = #Hospital
;
CREATE TABLE #HospList
( HospID smallint NULL )
SET #CommaLoc = charindex(',', #WorkHospList)
WHILE #CommaLoc > 1
BEGIN
SET #curVal = LEFT(#WorkHospList, #commaloc-1 )
INSERT INTO
#HospList( HospID )
SELECT #curVal
SET #WorkHospList = substring( #WorkHospList, #commaloc+1, len(#WorkHospList) )
SET #CommaLoc = charindex(',', #WorkHospList)
END
END
Begin
-- Sets the Baseline Price Date in the PriceChangeHistory Table.
With PC1
as
(Select
HospitalMasterID
,TxnCode
,UserInfoMasterID
,Active
,min(TxnDateTime) as StartingDate
From
PriceChangeHistory
Where
TxnDateTime Between #StartDate and #EndDate
Group By
HospitalMasterID, TxnCode, UserInfoMasterID, Active)
-- Gets the Baseline Price for the period from the PriceChangeHistory Table
,PC
as
(Select
PC1.HospitalMasterID
,PC1.TxnCode
,PC1.UserInfoMasterID
,PC1.Active
,Cast (PC1.StartingDate as Date) as StartingDate
,PC2.OldPrice as StartingPrice
,PC2.NewPrice
,PC2.TxnSubType
From
PC1
Inner Join
PriceChangeHistory as PC2
On
PC1.HospitalMasterID = PC2.HospitalMasterID
and
PC1.TxnCode = PC2.TxnCode
and
PC1.StartingDate = PC2.TxnDateTime
Where
PC2.OldPrice > PC2.NewPrice)
--MedicalHistory Information
,MH
as
(Select
HospitalMasterID
,PatientID
,TxnDate
,TxnCode
,Description
,ListAmount
,ExtendedAmount
,TxnType
,Quantity
,(Case
When Quantity <> '1' Then (ListAmount/Quantity)
Else ListAmount
End) as UnitPrice
From
MedicalHistory
Where
TxnDate Between #StartDate and #EndDate
and
_IsServOrITem = 1)
-- Determines the Revenue lost per each sale, also reduces the results to only those items where the Price was lowered not raised.
,RL
as
(Select
PC.HospitalMasterID
,MH.PatientID
,PC.TxnCode
,PC.TxnSubType
,MH.Description
,PC.UserInfoMasterID as ChangedByUserID
,MH.TxnDate
,PC.StartingPrice
,Cast (MH.UnitPrice as Money) as UnitPrice
,Cast ((StartingPrice - UnitPrice) as Money) as RevenueLost
From
PC
Left OUter Join
MH
on
PC.HospitalMasterID = MH.HospitalMasterID
and
PC.TxnCode = MH.TxnCode
Where
PC.StartingPrice > MH.UnitPrice)
--- Determine the name of the tech changing the prices.
,UI
as
(Select
HospitalMasterID
,UserInfoMasterID
,Name
From
UserInfo)
--- Get the Division and Hospital Name for each Hospital.
,HODI
as
(Select
DI.DivisionID
,DI.DivisionName
,HO.HospMastID
,HO.HospCode
,HO.HospName
From
ref_Hospital as HO
inner Join
ref_Division as DI
on
HO.DivisionID = DI.DivisionID)
,HI
as
(Select
HODI.DivisionID
,HODI.DivisionName
,RL.HospitalMasterID
,HODI.HospCode
,HODI.HospName
,RL.PatientID
,RL.TxnCode
,RL.TxnSubType
,RL.Description
,RL.ChangedByUserID
,RL.TxnDate
,RL.StartingPrice
,RL.UnitPrice
,RL.RevenueLost
From
RL
Left Outer Join
HODI
ON
RL.HospitalMasterID = HODI.HospMastID
Where
TXNDate Between #StartDate and #EndDate)
Select
*
From
HI
Where
HospitalMasterID in (Select HospID from #Hosplist)
Order By
HOspitalMasterID
end
Prior to SQL Server 2008, the standard way to filter by one or more values was to pass an XML document to the Stored Procedure and join on it. In this case, you could pass the data as a string with the integers separated by commas, then convert that into an XML document, then join on the XML. So you should change the multiselect in SSRS to a text datatype. Here's a post that shows you how to open an XML document: http://blog.sqlauthority.com/2009/02/13/sql-server-simple-example-of-reading-xml-file-using-t-sql/
SQL Server 2008 lets you use table-valued parameters, but again, it might be best to pass the data as a string of comma separated integers and then let the stored procedure put the data into a table-valued parameter, and then join on that. Here's a post that describes how to use table valued parameters: http://blog.sqlauthority.com/2008/08/31/sql-server-table-valued-parameters-in-sql-server-2008/

MySQL Count products from all subcategories

I have two tables; categories and products. For each category i would like to count how many products there are in all of its subcategories. I already have counted how many are in each category. Example tables are:
Categories:
ID ParentID ProductCount SubCategoryProducts
1 NULL 0
2 1 2
3 2 1
Products:
ProductID CategoryID
123 2
124 2
125 3
So i would like my function to make:
ID ParentID ProductCount SubCategoryProducts
1 NULL 0 3
2 1 2 1
3 2 1 0
It simply needs to be as a select query, no need to update the database.
Any ideas?
EDIT: SQL FIddle: http://sqlfiddle.com/#!2/1941a/4/0
If it were me I'd create a STORED PROCEDURE. The other option is to loop with PHP through the first query, then for each ID run another query - but this kind of logic can slow down your page drastically.
Here's a nice tutorial on stored procedures: http://net.tutsplus.com/tutorials/an-introduction-to-stored-procedures/
Basically you run the same loops I mentioned above you would with PHP (but it runs much faster). The procedure is stored in the database and can be called like a function. The result is the same as a query.
As requested, here's a sample procedure (or rather, it uses two) in my instance, "ags_orgs" acts in a similar way to your categories where there is a parentOrgID. "getChildOrgs" also acts kind of like a redundant function since I had no idea how many levels down I had to go (this was written for MSSQL - there are probably differences with mySQL) Unfortunately this doesn't count rows, rather it gets data. I highly recommend following a tutorial or two to get a better grip on how it works:
USE [dbname]
GO
/****** Object: StoredProcedure [dbo].[getChildOrgs] Script Date: 09/26/2012 15:30:06 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[getChildOrgs]
#myParentID int,
#isActive tinyint = NULL
AS
BEGIN
SET NOCOUNT ON
DECLARE #orgID int, #orgName varchar(255), #level int
DECLARE cur CURSOR LOCAL FOR SELECT orgID FROM dbo.ags_orgs WHERE parentOrgID = #myParentID AND isActive = ISNULL(#isActive, isActive) ORDER BY orderNum, orgName
OPEN cur
fetch next from cur into #orgID
WHILE ##fetch_status = 0
BEGIN
INSERT INTO #temp_childOrgs SELECT orgID,orgName,description,parentOrgID,adminID,isActive,##NESTLEVEL-1 AS level FROM dbo.ags_orgs WHERE orgID = #orgID
EXEC getChildOrgs #orgID, #isActive
-- get next result
fetch next from cur into #orgID
END
CLOSE cur
DEALLOCATE cur
END
GO
Which is called by this proc:
USE [dbname]
GO
/****** Object: StoredProcedure [dbo].[execGetChildOrgs] Script Date: 09/26/2012 15:29:34 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[execGetChildOrgs]
#parentID int,
#isActive tinyint = NULL,
#showParent tinyint = NULL
AS
BEGIN
CREATE TABLE #temp_childOrgs
(
orgID int,
orgName varchar(255),
description text,
parentOrgID int,
adminID int,
isActive tinyint,
level int
)
-- if this isn't AGS top level (0), make the first record reflect the requested organization
IF #parentID != 0 AND #showParent = 1
BEGIN
INSERT INTO #temp_childOrgs SELECT orgID,orgName,description,parentOrgID,adminID,isActive,0 AS level FROM dbo.ags_orgs WHERE orgID = #parentID
END
exec getChildOrgs #parentID, #isActive
SELECT * FROM #temp_childOrgs
DROP TABLE #temp_childOrgs
END
GO
Here is my procedure for counting products in all subcategories
DELIMITER $$
CREATE PROCEDURE CountItemsInCategories(IN tmpTable INT, IN parentId INT, IN updateId INT)
BEGIN
DECLARE itemId INT DEFAULT NULL;
DECLARE countItems INT DEFAULT NULL;
DECLARE done INT DEFAULT FALSE;
DECLARE recCount INT DEFAULT NULL;
DECLARE
bufItemCategory CURSOR FOR
SELECT
itemCategory.id AS id,
COUNT(CASE WHEN item.isVisible = 1 then 1 ELSE NULL END) items
FROM
itemCategory
LEFT JOIN item ON
item.categoryId = itemCategory.id
WHERE
itemCategory.isVisible = 1 AND itemCategory.categoryParentId = parentId
GROUP BY
itemCategory.id
ORDER BY
itemCategory.name;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
SET max_sp_recursion_depth = 10000;
IF tmpTable = 1 THEN
DROP TEMPORARY TABLE IF EXISTS tblResults;
CREATE TEMPORARY TABLE IF NOT EXISTS tblResults(
id INT NOT NULL PRIMARY KEY,
items INT
);
END IF;
OPEN bufItemCategory;
Reading_bufItemCategory: LOOP
FETCH FROM bufItemCategory INTO itemId, countItems;
IF done THEN
LEAVE Reading_bufItemCategory;
END IF;
IF tmpTable = 1 THEN
INSERT INTO tblResults VALUES(itemId, countItems);
ELSE
UPDATE tblResults SET items = items + countItems WHERE id = updateId;
END IF;
SET recCount = (SELECT count(*) FROM itemCategory WHERE itemCategory.categoryParentId = itemId AND itemCategory.isVisible = 1);
IF recCount > 0 THEN
CALL CountItemsInCategories(0, itemId, CASE WHEN updateId = 0 then itemId ELSE updateId END);
END IF;
END LOOP Reading_bufItemCategory;
CLOSE bufItemCategory;
IF tmpTable = 1 THEN
SELECT * FROM tblResults WHERE items > 0;
DROP TEMPORARY TABLE IF EXISTS tblResults;
END IF;
END $$
DELIMITER;
To call procedure just run:
CountItemsInCategories(firstLoop,parentId,updateId);
Where parameters are:
firstLoop - always "1" for first loop
parentId - parent of subcategories
updateId - id of row to update, always "0" for first loop
On example:
CountItemsInCategories(1,1,0);
I hope this example will be useful to someone.
This assumes you have
Product table named prods
prod_id|categ_id
and Category table named categ
categ_id|parent_categ_id
As you seem to be using Adjacency List structure where foreign key parent_categ_id column references prod_id column at the same table
the following query should work
select c1.categ_id,c1.parent_categ_id,count(prods.prod_id)
as product_count from categ c1
join prods on prods.categ_id=c1.categ_id or prods.categ_id
in( with recursive tree(id,parent_id)as
(select categ_id,parent_categ_id from categ
where categ_id=c1.categ_id
union all
select cat.categ_id,cat.parent_categ_id from categ cat
join tree on tree.id=cat.parent_categ_id) select id from tree)
group by c1.categ_id,c1.parent_categ_id
order by product_count
You can do this in one statement if you have a limit on the depth of the hierarchy. You said you only have 4 levels in total.
SELECT SUM(ProductCount)
FROM (
SELECT c0.ID, c0.ProductCount
FROM Categories AS c0
WHERE c0.ID = 1
UNION ALL
SELECT c1.ID, c1.ProductCount
FROM Categories AS c0
JOIN Categories AS c1 ON c0.ID = c1.ParentID
WHERE c0.ID = 1
UNION ALL
SELECT c2.ID, c2.ProductCount
FROM Categories AS c0
JOIN Categories AS c1 ON c0.ID = c1.ParentID
JOIN Categories AS c2 ON c1.ID = c2.ParentID
WHERE c0.ID = 1
UNION ALL
SELECT c3.ID, c3.ProductCount
FROM Categories AS c0
JOIN Categories AS c1 ON c0.ID = c1.ParentID
JOIN Categories AS c2 ON c1.ID = c2.ParentID
JOIN Categories AS c3 ON c2.ID = c3.ParentID
WHERE c0.ID = 1
) AS _hier;
That'll work for this query if you store the hierarchy in the way you're doing, which is called Adjacency List. Basically, the ParentID is the way each node records its position in the hierarchy.
There are a few other ways of storing hierarchies that allow for easier querying of whole trees or subtrees. The best data organization depends on which queries you want to run.
Here are some more resources:
Models for Hierarchical Data with SQL and PHP (user # RaymondNijland linked to it in a comment)
I gave that presentation as a webinar (free to view the recording, but requires registration).
My book, SQL Antipatterns Volume 1: Avoiding the Pitfalls of Database Programming.
What is the most efficient/elegant way to parse a flat table into a tree?