Using scalar valued function in MSSM with data from 2 tables - sql

I would like to here your advice on using a scalar valued function on MSSM that calculate average of data set coming 2 different table.
each table consist 12 columns that represent the values, in addition each table have 3 more columns to differ the data and when calculating the average these 3 columns should match (Product_ID, SalesRep_ID, and date (year)) on both tables
I Tried scalar valued function like this:
create FUNCTION [dbo].[Sales_BU_ScenarioB_PriceAv](#Product_ID nvarchar(255), #SalesRep_ID float, #Year float)
RETURNS decimal(18,0)
AS
BEGIN
DECLARE #Result decimal(18,0)
set #Result = (SELECT
case when
(
isnull(q.P01,0)
+ isnull(q.P02,0)
+ isnull(q.P03,0)
...
+ isnull(q.P12,0)
)
=0
then
0
else
(isnull(p.P01,0)*isnull(q.P01,0)
+ isnull(p.P02,0)*isnull(q.P02,0)
+ isnull(p.P03,0)*isnull(q.P03,0)
...
+ isnull(p.P12,0)*isnull(q.P12,0)
)
/
(
isnull(q.P01,0)
+ isnull(q.P02,0)
+ isnull(q.P03,0)
...
+ isnull(q.P12,0)
)
end
from DataEntrySalesQty$ q, DataEntrySalesPrice$ p
where #Year = p.Year and
#Product_ID = p.Product_ID and
#SalesRep_ID = p.SalesRep_ID)
RETURN #Result
END
this should calculate: sum((p.P01 * q.P01)....(p.P12 * q.P12)) and divide by: sum(q.P01,q.P02....q.P12) in order to get the average price for all relevant rows in these columns.
it looks like the function works when I run it, but when trying to test the result
select
Product_ID, SalesRep_ID, [Year], [dbo].[Sales_BU_ScenarioB_PriceAv](Product_ID, SalesRep_ID, [Year])
from DataEntrySalesQty$
I get the next error:
Msg 512, Level 16, State 1, Line 1
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
can you please help me understand what i am doing wrong?
thanks for all the helpers

I would recommend cross apply to unpivot the columns to rows, then aggregation:
select
q.product_id,
q.year,
q.salesrep_id,
sum(x.p * x.q) / sum(x.q) avg_price
from DataEntrySalesPrice$ AS p
inner join DataEntrySalesQty$ AS q
on q.product_id = p.product_id
and q.year = p.year
and q.salesrep_id = p.salesrep_id
cross apply (values
(p.p01, q.q01),
(p.p02, q.q02),
...,
(p.p12, q.q12)
) as x(p, q)
group by q.product_id, q.year, q.salesrep_id

Related

Incorrect Syntax near With

No matter where I place my With statement inside the SQL query, the keyword in the next line always shows an error, 'Incorrect syntax near keyword'. I also tried putting semi-colon.
; WITH Commercial_subset AS
(
SELECT DISTINCT
PRDID_Clean, Value, [Year]
FROM
Reporting_db_SPKPI.DBO.[tbl_RCCP_commercial]
WHERE
MEASURE = 'Unit Growth Rate'
)
--error appears at truncate
TRUNCATE TABLE Reporting_db_SPKPI.DBO.[tbl_RCCP_3_NR_dup]
Example 1:
[Example 1][1]
Example 2:
[Example 2][2]
What am I missing?
[1]: https://i.stack.imgur.com/lkfVd.png
[2]: https://i.stack.imgur.com/tZRnG.png
My Final code after getting suggestions in the comments,
--Ensure the correct database is selected for creating the views
USE Reporting_db_SPKPI
--Create the table where new values will be appended
Insert into Reporting_db_SPKPI.DBO.[tbl_RCCP_3_NR_dup]
Select *, Replace(productID,'-','') as ProductID_clean from Reporting_db_SPKPI.DBO.[tbl_RCCP_3_NR]
GO
--Create a subset as view which will be used for join later
Create or Alter View QRY_Commerical_Subset AS
Select Distinct PRDID_Clean, Value, [Year] From Reporting_db_SPKPI.DBO.[tbl_RCCP_commercial] where MEASURE = 'Unit Growth Rate'
Go
--Create a view with distinct list of all SKUs
CREATE OR ALTER VIEW QRY_RCCP_TEMP AS
SELECT
PRODUCTID, ROW_NUMBER() Over (ORDER BY ProductID) AS ID
FROM (
SELECT
DISTINCT A.ProductID_clean ProductID
FROM
Reporting_db_SPKPI.DBO.[tbl_RCCP_3_NR_dup] A
LEFT JOIN
Reporting_db_SPKPI.DBO.QRY_Commerical_Subset B ON A.ProductID_clean = B.PRDID_Clean
WHERE
B.PRDID_Clean IS NOT NULL --and A.filename = 'Capacity Planning_INS_Springhill' --DYNAMIC VARIABLE HERE
and Cast(A.SnapshotDate as date) =
(SELECT Max(Cast(SnapshotDate as date)) FROM reporting_db_spkpi.dbo.tbl_RCCP_3_NR)
) T
GO
SET NOCOUNT ON
-- For every product id from the distinct list iterate the following the code
DECLARE #I INT = 1
WHILE #I <= (SELECT MAX(ID) FROM QRY_RCCP_TEMP)
BEGIN
DECLARE #PRODUCT NVARCHAR(50) = (SELECT PRODUCTID FROM QRY_RCCP_TEMP WHERE ID = #I)
DROP TABLE Reporting_db_SPKPI.DBO.[tbl_RCCP_3_NR_temp]
--Retrieve last 12 months of value from NR and add it to a temp table in increasing order of their months. These 12 data points will be baseline
SELECT
Top 12 A.*,
Case When B.[Value] is Null then 0 else CAST(B.[Value] as float) End GROWTH
INTO
Reporting_db_SPKPI.DBO.[tbl_RCCP_3_NR_temp]
FROM
Reporting_db_SPKPI.DBO.[tbl_RCCP_3_NR_dup] A
LEFT JOIN
--using the view here
QRY_Commerical_Subset B ON B.PRDID_Clean = A.ProductID_clean AND B.[YEAR] = YEAR(A.[MONTH])+1
WHERE
A.PRODUCTID= #PRODUCT
AND Cast(A.SnapshotDate AS DATE) = (SELECT Max(Cast(SnapshotDate AS DATE)) FROM reporting_db_spkpi.dbo.[tbl_RCCP_3_NR_dup])
Order by
[Month] desc
-- Generate 3 years of data
DECLARE #J INT = 1
WHILE #J<=3
BEGIN
--Calculate next year's value
UPDATE Reporting_db_SPKPI.DBO.[tbl_RCCP_3_NR_temp]
SET
[Value] = [Value]*(1+ GROWTH),
[MONTH] = DATEADD(YEAR,1,[Month]),
MonthCode= 'F' + CAST(CAST(SUBSTRING(MonthCode,2,LEN(MonthCode)) AS INT) + 12 AS NVARCHAR(10))
--Add it to the NR table.
Insert into Reporting_db_SPKPI.DBO.[tbl_RCCP_3_NR_dup]
(ProductID, MonthCode, Value, Month, FileName,
LastModifiedDate, SnapshotDate, Quarter, IsError, ErrorDescription)
Select
ProductID, MonthCode, Value, Month, FileName,
LastModifiedDate, SnapshotDate, Quarter, IsError, ErrorDescription
from
Reporting_db_SPKPI.DBO.[tbl_RCCP_3_NR_temp]
--Update growth rate for next year
UPDATE Reporting_db_SPKPI.DBO.[tbl_RCCP_3_NR_temp]
SET GROWTH = Case When B.[Value] is Null then 0 else CAST(B.[Value] as float) End
FROM Reporting_db_SPKPI.DBO.QRY_Commerical_Subset B
WHERE B.PRDID_Clean = ProductID_clean AND [YEAR] = YEAR([MONTH])+1
SET #J=#J+1
END
SET #I=#I+1
END
DROP VIEW QRY_RCCP_TEMP
DROP VIEW QRY_Commerical_Subset
The WITH is a Common Table Expression, aka CTE.
And a CTE is like a template for a sub-query.
For example this join of the same sub-query:
SELECT *
FROM (
select distinct bar
from table1
where foo = 'baz'
) AS foo1
JOIN (
select distinct bar
from table1
where foo = 'baz'
) AS foo2
ON foo1.bar > foo2.bar
Can be written as
WITH CTE_FOO AS (
select distinct bar
from table1
where foo = 'baz'
)
SELECT *
FROM CTE_FOO AS foo1
JOIN CTE_FOO AS foo2
ON foo1.bar > foo2.bar
It's meant to be used with a SELECT.
Not with a TRUNCATE TABLE or DROP TABLE.
(It can be used with an UPDATE though)
As such, treat the TRUNCATE as a seperate statement.
TRUNCATE TABLE Reporting_db_SPKPI.DBO.[tbl_RCCP_3_NR_dup];
WITH Commercial_subset AS
(
SELECT DISTINCT
PRDID_Clean, Value, [Year]
FROM
Reporting_db_SPKPI.DBO.[tbl_RCCP_commercial]
WHERE
MEASURE = 'Unit Growth Rate'
)
SELECT *
FROM Commercial_subset;
Btw, the reason why many write a CTE with a leading ; is because the WITH clause raises an error if the previous statement wasn't ended with a ;. It's just a small trick to avoid that error.

T-SQL IN Clause Combined With Case Statement

i have query where i have a in clause combined with a case statement.
the error i get is this:
Subquery returned more than 1 value. This is not permitted when the
subquery follows =, !=, <, <= , >, >= or when the subquery is used as
an expression.
i receive in stored procedure a concatenate varchar value like this '2,3,4,5' and i want in my query to filter the ids using the in clause
but something i missing
someone can give me a hand with this pls?
#ids varchar(50) = '2,3,4'
DECLARE #mylist TABLE (Id int)
INSERT INTO #mylist
select CONVERT(INT,v) from [dbo].[SplitString](#cardtypes)
//result of #mylist
//-------------------
//2
//3
//4
and then in the query
select * from mytable
where
ctable.Id IN (CASE
WHEN ISNULL(#ids,'') <> '' THEN (select id from #mylist)
ELSE ctable.Id
END)
Note:
ctable.Id (INT)
If i have only passing one value it works but if there are more it breaks
thanks in advance
You're trying to mix scalar value ctable.Id and some resultset select id from #mylist here in one case statement.
This can't be done, but you can rewrite your query like this:
select * from mytable
where
(isnull(#ids,'') <> '' and ctable.Id in (select id from #mylist))
or isnull(#ids,'') = ''
Out of curiosity, why are you using a temporary table? You can express this as a CTE:
with values as (
select CONVERT(INT, v)
from [dbo].[SplitString](#cardtypes)
)
select t.*
from <table> t
where t.id in (select v from values) or
not exists (select 1 from values);

SQL Server Scalar Function with "IN" Getting Subquery Returned more than 1 Error

Here's the query which works well:
select MIN(dbo.GetDiscountedPrice(P.AutoID))
FROM Product P
where P.AutoID in (2910,2912,2820)
It returns the cheapest price among 2910,2912 and 2820 (AutoID is the primary key)
BUT this query returns an error:
select MIN(dbo.GetDiscountedPrice(P.AutoID))
FROM Product P
where P.AutoID in (SELECT AutoID FROM Product WHERE Category=2)
Error:
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
This query actually returns 2910,2912,2820
SELECT AutoID FROM Product WHERE Category=2
Why is that so and how can I achieve the purpose with by getting products based on category?
Function GetDiscountedPrice:
It's just a very simple function which:
Gets the price
Gets the percentage
Returns the discounted price after minus off the percentage
Note that percentage was taken from another table called ProductDiscount.
CREATE FUNCTION [dbo].[GetDiscountedPrice]
(
#ProductAutoID bigint
)
RETURNS decimal(10,4)
AS
BEGIN
DECLARE #Result decimal(10,4)
Declare #Price float = (select Price
from Product
where AutoID = #ProductAutoID)
Declare #DP float = ISNULL
(
(select DiscountPercentage
from ProductDiscount
where Product_AutoID = #ProductAutoID), 0)
set #Result = #Price - (#Price * 0.01 * #DP)
RETURN #Result
END
If statement
where P.AutoID in (SELECT AutoID FROM Product WHERE Category=2)
fails but
where P.AutoID in (2910,2912,2820)
works, then the problem is definitely in function itself, because first statement can not produce mentioned error. I.e. first statement returns different set then second statement and data produces that error probably here:
Declare #Price float = (select Price
from Product
where AutoID = #ProductAutoID)
and here:
(select DiscountPercentage
from ProductDiscount
where Product_AutoID = #ProductAutoID)
Those subqueries are returning more then 1 row, so the error occures.
try this code (i did not check it)-
DECLARE #tbl TABLE (id int IDENTITY(1,1),AutoID int)
INSERT INTO #tbl
SELECT AutoID FROM Product WHERE Category=2
DECLARE #x int=1
DECLARE #y int=1
WHILE #x <= (SELECT count (*) from #tbl)
BEGIN
set #y=(select AutoID FROM #tbl WHERE id=#x)
select MIN(dbo.GetDiscountedPrice(#y))
FROM Product P
SET #x=#x+1
END
Based on everything you're saying, this should work:
select MIN(AutoID)
FROM Product
where Category=2
Q: does this work?
select GetDiscountedPrice(MIN(AutoID))
FROM Product
where Category=2
Q: If not, could you please post "GetDiscountedPrice"?
The problem lies on the function, because it exists more than 1 DiscountPercentage for a single product. Adding "DISTINCT" in the function when selecting the DiscountPercentage eliminates the duplicate data.
Declare #DP float = ISNULL
(
(select DISTINCT DiscountPercentage
from ProductDiscount
where Product_AutoID = #ProductAutoID), 0)

Group data without changing query flow

For me it's hard to explait what do I want so article's name may be unclear, but I hope I can describe it with code.
I have some data with two most important value, so let it be time t and value f(t). It's stored in the table, for example
1 - 1000
2 - 1200
3 - 1100
4 - 1500
...
I want to plot a graph using it, and this graph should contain N points. If table has rows less than this N, then we just return this table. But if it hasn't, we should group this points, for example, N = Count/2, then for an example above:
1 - (1000+1200)/2 = 1100
2 - (1100+1500)/2 = 1300
...
I wrote an SQL script (it works fine for N >> Count) (MonitoringDateTime - is t, and ResultCount if f(t))
ALTER PROCEDURE [dbo].[usp_GetRequestStatisticsData]
#ResourceTypeID bigint,
#DateFrom datetime,
#DateTo datetime,
#EstimatedPointCount int
AS
BEGIN
SET NOCOUNT ON;
SET ARITHABORT ON;
declare #groupSize int;
declare #resourceCount int;
select #resourceCount = Count(*)
from ResourceType
where ID & #ResourceTypeID > 0
SELECT d.ResultCount
,MonitoringDateTime = d.GeneratedOnUtc
,ResourceType = a.ResourceTypeID,
ROW_NUMBER() OVER(ORDER BY d.GeneratedOnUtc asc) AS Row
into #t
FROM dbo.AgentData d
INNER JOIN dbo.Agent a ON a.CheckID = d.CheckID
WHERE d.EventType = 'Result' AND
a.ResourceTypeID & #ResourceTypeID > 0 AND
d.GeneratedOnUtc between #DateFrom AND #DateTo AND
d.Result = 1
select #groupSize = Count(*) / (#EstimatedPointCount * #resourceCount)
from #t
if #groupSize = 0 -- return all points
select ResourceType, MonitoringDateTime, ResultCount
from #t
else
select ResourceType, CAST(AVG(CAST(#t.MonitoringDateTime AS DECIMAL( 18, 6))) AS DATETIME) MonitoringDateTime, AVG(ResultCount) ResultCount
from #t
where [Row] % #groupSize = 0
group by ResourceType, [Row]
order by MonitoringDateTime
END
, but it's doesn't work for N ~= Count, and spend a lot of time for inserts.
This is why I wanted to use CTE's, but it doesn't work with if else statement.
So i calculated a formula for a group number (for use it in GroupBy clause), because we have
GroupNumber = Count < N ? Row : Row*NumberOfGroups
where Count - numer of rows in the table, and NumberOfGroups = Count/EstimatedPointCount
using some trivial mathematics we get a formula
GroupNumber = Row + (Row*Count/EstimatedPointCount - Row)*MAX(Count - Count/EstimatedPointCount,0)/(Count - Count/EstimatedPointCount)
but it doesn't work because of Count aggregate function:
Column 'dbo.AgentData.ResultCount' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
My english is very bad and I know it (and i'm trying to improve it), but hope dies last, so please advice.
results of query
SELECT d.ResultCount
, MonitoringDateTime = d.GeneratedOnUtc
, ResourceType = a.ResourceTypeID
FROM dbo.AgentData d
INNER JOIN dbo.Agent a ON a.CheckID = d.CheckID
WHERE d.GeneratedOnUtc between '2015-01-28' AND '2015-01-30' AND
a.ResourceTypeID & 1376256 > 0 AND
d.EventType = 'Result' AND
d.Result = 1
https://onedrive.live.com/redir?resid=58A31FC352FC3D1A!6118&authkey=!AATDebemNJIgHoo&ithint=file%2ccsv
Here's an example using NTILE and your simple sample data at the top of your question:
declare #samples table (ID int, sample int)
insert into #samples (ID,sample) values
(1,1000),
(2,1200),
(3,1100),
(4,1500)
declare #results int
set #results = 2
;With grouped as (
select *,NTILE(#results) OVER (order by ID) as nt
from #samples
)
select nt,AVG(sample) from grouped
group by nt
Which produces:
nt
-------------------- -----------
1 1100
2 1300
If #results is changed to 4 (or any higher number) then you just get back your original result set.
Unfortunately, I don't have your full data nor can I fully understand what you're trying to do with the full stored procedure, so the above would probably need to be adapted somewhat.
I haven't tried it, but how about instead of
select ResourceType, CAST(AVG(CAST(#t.MonitoringDateTime AS DECIMAL( 18, 6))) AS DATETIME) MonitoringDateTime, AVG(ResultCount) ResultCount
from #t
where [Row] % #groupSize = 0
group by ResourceType, [Row]
order by MonitoringDateTime
perhaps something like
select ResourceType, CAST(AVG(CAST(#t.MonitoringDateTime AS DECIMAL( 18, 6))) AS DATETIME) MonitoringDateTime, AVG(ResultCount) ResultCount
from #t
group by ResourceType, convert(int,[Row]/#groupSize)
order by MonitoringDateTime
Maybe that points you in some new direction? by converting to int we are truncating everything after the decimal so Im hoping that will give you a better grouping? you might need to put your row-number over resource type for this to work?

Sql server exception: the statement did not return a result set

I have a SQL query that runs fine in SQL Server Management Studio, but when I copy and paste it into jasperreports's iReport to make a report, it gives me a SQL Server exception and says the statement did not return a result set. This has left me confused.
The query is:
declare #index int = 1
declare #t Table(ID INT, DI INT, INDBOOK1 INT, INDBOOK2 INT, delta INT)
while(#index < 18)
begin
INSERT INTO #t
select distinct top 18
col1.ID,
col1.DI,
col1.INDBOOK as INDBOOK1,
col2.INDBOOK as INDBOOK2,
col2.INDBOOK - col1.INDBOOK
FROM
table as col1
inner join
table as col2 on col2.ID = #index
and col2.DI = col1.DI+1
where
col1.ID = #index
set #index = #index + 1
end
select ID, DI, INDBOOK1, INDBOOK2, delta FROM #t
Does anybody know why this is giving me the no result set returned exception?
Any help appreciated.
Tough to tell without sample data etc. but I think this should be close to what you need, in a single statement with no explicit loops:
;WITH x([index]) AS
(
SELECT TOP (18) ROW_NUMBER() OVER (ORDER BY number)
FROM master..spt_values ORDER BY number
),
y AS
(
SELECT [index] = ROW_NUMBER() OVER (PARTITION BY col1.ID ORDER BY col1.ID),
col1.ID, col1.DI, col1.INDBOOK as INDBOOK1, col2.INDBOOK as INDBOOK2,
col2.INDBOOK - col1.INDBOOK as delta
FROM dbo.table as col1
INNER JOIN dbo.table as col2
ON col2.ID = col1.ID
AND col2.DI = col1.DI+1
)
SELECT y.ID, y.DI, y.INDBOOK1, y.INDBOOK2, y.delta
FROM x INNER JOIN y
ON x.[index] = y.[index]
WHERE y.[index] <= 18;