Firstly, may I state that I'm aware of the ability to, e.g., create a new function, declare variables for rowcount1 and rowcount2, run a stored procedure that returns a subset of rows from a table, then determine the entire rowcount for that same table, assign it to the second variable and then 1 / 2 x 100....
However, is there a cleaner way to do this which doesn't result in numerous running of things like this stored procedure? Something like
select (count(*stored procedure name*) / select count(*) from table) x 100) as Percentage...
Sorry for the crap scenario!
EDIT: Someone has asked for more details. Ultimately, and to cut a very long story short, I wish to know what people would consider the quickest and most processor-concise method there would be to show the percentage of rows that are returned in the stored procedure, from ALL rows available in that table. Does that make more sense?
The code in the stored procedure is below:
SET #SQL = 'SELECT COUNT (DISTINCT c.ElementLabel), r.FirstName, r.LastName, c.LastReview,
CASE
WHEN c.LastReview < DateAdd(month, -1, GetDate()) THEN ''OUT of Date''
WHEN c.LastReview >= DateAdd(month, -1, GetDate()) THEN ''In Date''
WHEN c.LastReview is NULL THEN ''Not Yet Reviewed'' END as [Update Status]
FROM [Residents-'+#home_name+'] r
LEFT JOIN [CarePlans-'+#home_name+'] c ON r.PersonID = c.PersonID
WHERE r.Location = '''+#home_name+'''
AND CarePlanType = 0
GROUP BY r.LastName, r.FirstName, c.LastReview
HAVING COUNT(ELEMENTLABEL) >= 14
Thanks
Ant
I could not tell from your question if you are attempting to get the count and the result set in one query. If it is ok to execute the SP and separately calculate a table count then you could store the results of the stored procedure into a temp table.
CREATE TABLE #Results(ID INT, Value INT)
INSERT #Results EXEC myStoreProc #Parameter1, #Parameter2
SELECT
Result = ((SELECT COUNT(*) FROM #Results) / (select count(*) from table))* 100
I have two tables one of them have historical(cdr_hist) data other table have data from today(cdr_stage). My script must run every 30 minutes and calculate data from last 4 hours but every night at 12 all data move at cdr_hist.
The question is how I can switch and take data from history table when script run at 12:00 because cdr_stage is empty...
I tried this:
IF OBJECT_ID ('[**CDR_Stage**]') IS NOT NULL
BEGIN
Select.....
From **CDR_Stage**
END
ELSE
Select.....
From **CDR_Hist**
END
But its not work correctly...
Any ideas??
No need for IFs , that can be done with pure sql using UNION and NOT EXISTS() :
SELECT * FROM CDR_Stage
UNION ALL
SELECT * FROM CDR_Hist
WHERE NOT EXISTS(SELECT 1 FROM CDR_Stage) -- Second select will return data only if first one won't .
You need to check the record existence instead of table existence
IF EXISTS (SELECT 1
FROM CDR_Stage)
SELECT *
FROM CDR_Stage
ELSE
SELECT *
FROM CDR_Hist
Or Dynamic Sql
DECLARE #sql VARCHAR(4000)
SET #sql = 'select * from '
+ CASE
WHEN EXISTS (SELECT 1
FROM CDR_Stage) THEN 'CDR_Stage'
ELSE 'CDR_Hist'
END
EXEC (#sql)
I have this query in a stored procedure:
SELECT
*,
ISNULL(dbo.ReturnShortageByItemCodeLinePackage(LineId, TestPackageId, MaterialDescriptionId), 0) AS Shortage
FROM
dbo.ViewMTO
I am using a function inside the query to calculate an integer value as you can see here :
ALTER FUNCTION [dbo].[ReturnShortageByItemCodeLinePackage]
(#lineId int,#testpackId int, #MaterialDescriptionId int)
RETURNS float
AS
BEGIN
DECLARE #shortageQuantity float
DECLARE #MIVQuantity float
DECLARE #totalQuantity float
DECLARE #spoolQuantity float
DECLARE #ExistInSiteQuantity float
DECLARE #BeforeDoneQuantity float
SELECT
#totalQuantity = Quantity,
#spoolQuantity = QuantitySpool,
#ExistInSiteQuantity = QuantityExistInSite,
#BeforeDoneQuantity = QuantityBeforeDone
FROM
[SPMS2].[dbo].Materials
WHERE
LineId = #lineId
AND TestPackageId = #testpackId
AND MaterialDescriptionId = #MaterialDescriptionId
SELECT
#MIVQuantity = SUM(QuantityDeliver)
FROM
MaterialIssueVoucherDetails miv
JOIN
MaterialRequestContractorDetails mrc ON miv.MaterialRequestContractorDetailId = mrc.Id
WHERE
TestPackageId = #testpackId
AND LineId = #lineId
AND miv.MaterialDescriptionId = #MaterialDescriptionId
IF #MIVQuantity IS NULL
BEGIN
SET #MIVQuantity = 0
END
SET #shortageQuantity = #totalQuantity - (#BeforeDoneQuantity + #ExistInSiteQuantity + #spoolQuantity + #MIVQuantity)
RETURN round(#shortageQuantity, 3)
END
My query is executed in 3 minutes, it is catastrophic for my users! Is there any better solution?
I can recommend three things:
A. The following line..
SELECT #totalQuantity= ...
FROM [SPMS2].[dbo].Materials
Is this accessing a different database via a Linked Server connection ? How fast is this connection ?
B. Your SP contains two SELECT statements. Which of them is the bottleneck ?
You can add some PRINT statements to show when each is started:
PRINT convert(nvarchar, GetDate(), 108) + ' This is the time !'
C. Try running the SQL show on my webpage below, which will highlight missing Indexes.
Find missing indexes
Hope this helps.
Convert your Scaler function to Table-Valued function, and then place the function in FROM clause for LEFT JOIN. Do check execution plans to find any warning.
Testing performance of Scalar vs Table-valued functions in sql server
I have a stored procedure which drops a table if it exists, then it re-creates the table & fills it with relevant data, a friend of mine has about the same code, the only real difference is in the column headers for the table.
As an illustration, here's how mine looks (not really, just a representation).
+----+-----+-----+--------+
| ID | Foo | Bar | Number |
+----+-----+-----+--------+
| 1 | x | x | 0 |
| 2 | x | x | 1 |
+----+-----+-----+--------+
And here's what his might look like
+----+--------+--------+-----+--------+
| ID | BarFoo | FooBar | Num | Suffix |
+----+--------+--------+-----+--------+
| 1 | x | x | 0 | a |
| 2 | x | x | 1 | b |
+----+--------+--------+-----+--------+
Again, these are merely representations of the situation.
As this is to be a school assignment, the teacher will be creating & executing both SP's, however when creating the SP after using another, I get this error:
Msg 207, Level 16, State 1, Procedure XYZ, Line 59
Invalid column name 'Foo'.
Msg 213, Level 16, State 1, Procedure XYZ, Line 61
Column name or number of supplied values does not match table definition.
However, at the start of both stored procedures, we have this:
CREATE PROCEDURE XYZ
AS
BEGIN
IF EXISTS (SELECT name
FROM sysobjects
WHERE name = 'TABLENAME'
AND xtype = 'u')
DROP TABLE TABLENAME;
From what I understand, this should remove the entire table? Including table/column definitions & data?
The only fix I've found so far, is to either execute the DROP TABLE separately before creating the stored procedure, which won't work for us as it really has to be within the stored procedure.
Help would be much appreciated :)
EDIT: Here's my ACTUAL code, apart from comments, this is exactly how it looks in my script (excluding other code behind it).
IF EXISTS (SELECT name
FROM sysobjects
WHERE name = 'BerekenStatistiek'
AND xtype = 'p')
DROP PROCEDURE BerekenStatistiek;
GO
CREATE PROCEDURE BerekenStatistiek
#jaar INT=0
AS
BEGIN
IF EXISTS (SELECT name
FROM sysobjects
WHERE name = 'Statistiek'
AND xtype = 'u')
DROP TABLE Statistiek;
DECLARE #year AS NVARCHAR (4);
SET #year = CONVERT (NVARCHAR (4), #jaar);
SELECT *,
CAST (Kost - Korting + Freight AS MONEY) AS Netto,
'' AS Richting
INTO Statistiek
FROM (SELECT O.Kwartaal,
CAST (SUM(O.Kost) AS MONEY) AS Kost,
CAST (SUM(O.Korting) AS MONEY) AS Korting,
CAST (SUM(O.Freight) AS MONEY) AS Freight
FROM (SELECT CASE
WHEN CONVERT (NVARCHAR (8), OrderDate, 112) BETWEEN #year + '0101' AND #year + '0331' THEN 1
WHEN CONVERT (NVARCHAR (8), OrderDate, 112) BETWEEN #year + '0401' AND #year + '0630' THEN 2
WHEN CONVERT (NVARCHAR (8), OrderDate, 112) BETWEEN #year + '0701' AND #year + '0930' THEN 3
WHEN CONVERT (NVARCHAR (8), OrderDate, 112) BETWEEN #year + '1001' AND #year + '1231' THEN 4
END AS 'Kwartaal',
ROUND(UnitPrice * Quantity, 2) AS Kost,
Round((UnitPrice * Quantity) * Discount, 2) AS Korting,
Freight
FROM Orders AS O
INNER JOIN
OrderDetails AS Od
ON O.OrderID = Od.OrderID
WHERE CONVERT (NVARCHAR (4), OrderDate, 112) = #year) AS O
GROUP BY O.Kwartaal) AS O1;
ALTER TABLE Statistiek ALTER COLUMN Kwartaal INT NOT NULL;
ALTER TABLE Statistiek ALTER COLUMN Richting NVARCHAR (8);
ALTER TABLE Statistiek
ADD PRIMARY KEY (Kwartaal);
...
And here's his code (the insertion of values in the variables are excluded just for readability (his code is a bit more bulky):
IF EXISTS (SELECT name
FROM sysobjects
WHERE name = 'BerekenStatistiek'
AND xtype = 'p')
BEGIN
DROP PROCEDURE BerekenStatistiek;
END
GO
CREATE PROCEDURE BerekenStatistiek
#jaartal INT
AS
BEGIN
DECLARE #huidigkwartaal AS INT = 1;
DECLARE #beginmaand AS INT;
DECLARE #eindmaand AS INT;
DECLARE #vorige_netto_ontvangsten AS MONEY;
IF EXISTS (SELECT *
FROM sysobjects
WHERE name = 'Statistiek'
AND xtype = 'U')
BEGIN
DROP TABLE Statistiek;
END
CREATE TABLE Statistiek
(
kwartaalnummer INT ,
beginmaand INT ,
eindmaand INT ,
orderbedrag MONEY ,
korting MONEY ,
vervoerskost MONEY ,
netto_ontvangsten MONEY ,
stijgend_dalend_gelijk NVARCHAR (10)
);
--Variables get their data here.
INSERT INTO Statistiek (kwartaalnummer, beginmaand, eindmaand, orderbedrag, korting, vervoerskost, netto_ontvangsten, stijgend_dalend_gelijk)
VALUES (#huidigkwartaal, #beginmaand, #eindmaand, #orderbedrag, #korting, #vervoerskost, #netto_ontvangsten, #stijgend_dalend_gelijk);
"however when creating the SP after using another, I get this error" (Emphasis added.)
SQL Server will insist that a stored procedure match the definitions of tables that exist as the time the stored procedure is created. If the table does not exist when the stored procedure is created, SQL Server will assume that a matching table will appear at run time.
create table t (c int)
go
create procedure p as begin
drop table t
select 1 as diff_column_name into t
select diff_colun_name from t
end
results in:
Msg 207, Level 16, State 1, Procedure p, Line 6
Invalid column name 'diff_colun_name'.
Now, drop table t, and the procedure cane be created:
drop table t
go
create procedure p as begin
drop table t
select 1 as diff_column_name into t
select diff_colun_name from t
end
Command(s) completed successfully.
If you can use a different table name, start with that. And, if the table has to exist only for a moment after the proc is executed so that it can be selected from, then create a global temporary table (i.e. table name starts with ## as in ##MyTable).
However, if it is a requirement to use the same table name as your classmate, then the teacher is probably trying to get you to learn about deferred object resolution (i.e. #Shannon's answer) and how to get around it, because outside of learning this, the scenario makes no sense since one would never do such a thing in reality.
Sub-processes (i.e. EXEC and sp_executesql) do not resolve immediately since they aren't executed when creating the stored procedure. So, simplistically, just declare a new NVARCHAR(MAX) variable to hold some Dynamic SQL and put your SELECT statement in there. Use sp_executesql to pass in the #year variable. You are creating a real table so it will survive beyond the subprocess ending and then the ALTER TABLE statement will work.
Additional notes:
You don't really need the ALTER statement to set the datatype of the [Richting] field. Just tell SQL Server what the type is in your SELECT statement:
CONVERT(NVARCHAR(8), '') AS [Richting]
You don't really want to do CONVERT(NVARCHAR(8), OrderDate, 112) to compare to a value as it invalidates the use of any indexes that might be on [OrderDate]. Instead, construct a date value from the strings and convert that to a DATETIME or DATE (i.e. CONVERT(DATETIME, #year + '0101')).
To better understand this issue, please read Sargability: Why %string% Is Slow, and at least the first link at the bottom, which is: What makes a SQL statement sargable?
You don't really want to convert the OrderDate field to NVARCHAR(4) just to compare the year, for the same reason as just mentioned in the above point. At the very least using the YEAR() function would be more direct. But if you want to make sure indexes can be used, you can't put a function on the field. But you only want the year. So isn't the year the same as BETWEEN #Year + '0101' AND #Year + '1231'? ;-)
Interestingly enough, the first example in the accepted answer in the "What makes a SQL statement sargable?" S.O. question linked in the previous bullet is exactly what I am recommending here :).
For I can understand, the wrong queries are the inserts, because the engine can't find correct table structure, check if the inserts have the same structure of your second table example. Dont forget to check the USE at the beginning of the script, maybe you are using a different db, this can happen :).
In the last bit of code, you are having
AND xtype = 'U'
If your collation is case sensitive, the drop is not taking place and thus the error.
I have a table like this in my sql server:
TransactID Dtid Sid
1086 5 4
1086 7 8
1086 4 3
i want to take corresponding name of Dtid and Sid from resepective table.so i created stored procedure like this:
ALTER procedure [dbo].[PDTDamageFetch]
#Carid NVARCHAR(50)
AS
BEGIN
SET NOCOUNT ON
declare #transid integer,#dtid integer,#sid integer,#Damage nvarchar(100),#side nvarchar(100)
select #transid= t.transactID from Transaction_tbl t where t.TBarcode=#Carid
select #dtid= tr.Dtid from TransactDamageAssign_tbl tr where tr.transactID=#transid
select #sid= tr.sid from TransactDamageAssign_tbl tr where tr.transactID=#transid
select #Damage=dt.DtName from DamageType_tbl dt where dt.Dtid=#dtid
select #side= ds.SName from DamageSide_tbl ds where ds.Sid=#sid
select #Damage,#side
end
then i executed my stored procedure passing corresponding carid..transactID of particular car id is 1086 but stored procedure always showing one row of data..how i can replicate my issue..my
expected out put at this stage is:
Damage Side
scrtch front
broken Back
damged side
Try this query instead of yours. You are just assining one value to a variable, and that's why you just get one row
SELECT * FROM Transaction_tbl t
INNER JOIN TransactDamageAssign_tbl tr ON tr.transactID = t.transactID
INNER JOIN DamageType_tbl dt ON dt.Dtid = tr.Dtid
INNER JOIN DamageSide_tbl ds ON ds.Sid = tr.sid
WHERE t.TBarcode=#Carid
I think you should try table variables or temp tables to list all records.
The way you are trying, will always return the very last row of matching condition.