How to reflect on SQL Server sps and funcs using sys tables? - tsql

Where do I look in Microsoft SQL Server system tables to find info about the parameters a builtin stored procedure or function takes?

Looks like a join on sys.system_objects and sys.system_paramters will do it. This should get you started:
SELECT ob.object_id, ob.name, ob.is_ms_shipped, ob.type_desc, pa.*
from sys.system_objects ob
inner join sys.all_parameters pa
on pa.object_id = ob.object_id
Reset with the columns you're interested in and you should be good.
Make it a left outer join to pick up objects that have no parameters.

Here is sql Function that returns parameter info of a given routine
ALTER Function [dbo].[ftRoutineSchema](#RoutineName varchar(200)) returns table as return
--declare #routineName varchar(100);select #routineName='ftDetailsOfLogin'
SELECT ColumnName=Case Is_Result
When 'YES' then '#RC'
else Parameter_Name
end
,DataType= case Data_Type
When 'DECIMAL' then 'Decimal('+convert(varchar,Numeric_precision)+','+Convert(varchar,Numeric_scale)+')'
When 'numeric' then 'Decimal('+convert(varchar,Numeric_precision)+','+Convert(varchar,Numeric_scale)+')'
when 'varchar' then 'Varchar('+Convert(varchar,Character_maximum_length)+')'
ELSE dATA_TYPE
end
,ColumnOrder=Ordinal_Position
,Direction =Case Parameter_Mode
when 'INOUT' then 'Out'
else Parameter_Mode
end
FROM --INFORMATION_SCHEMA.ROUTINE_cOLUMNS
Information_schema.Parameters
--WHERE TABLE_NAME=#routineName --order by columnorder
Where Specific_name=#ROUTINEnAME

Related

How do I avoid syntax error in FOR loops in postgresql?

I'm using PostgreSQL 10.5 and I have the following SQL:
FOR temprow IN
SELECT o.objectid, o.nametag, cor.userseqno, cor.commseqno
FROM "commuserobjectrights" as cor
LEFT JOIN "object" as o ON cor.objectid = o.objectid
WHERE o.nametag LIKE 'commission.video_questions'
LOOP
INSERT INTO u commuserobjectrights (objectid, commseqno, userseqno, access)
VALUES (temprow.objectid, temprow.commseqno, temprow.userseqno, TRUE);
END LOOP;
which throws the following error:
ERROR: syntax error at or near "FOR" Position: 3
I have never used loops before but according the documentation, postgresql should have support for these types of loops. And yes, I have checked and double checked that all tables and column names are spelled correctly.
You can't use FOR loops outside of procedural code. But, in general Postgres (and SQL) is optimized to already do set based operations. So, you may phrase this as an INSERT INTO ... SELECT:
INSERT INTO commuserobjectrights (objectid, commseqno, userseqno, access)
SELECT o.objectid, o.nametag, cor.userseqno, TRUE
FROM "commuserobjectrights" as cor
LEFT JOIN "object" as o ON cor.objectid = o.objectid
WHERE o.nametag LIKE 'commission.video_questions';
FOR is procedural code, so you need to use DO or use it in stored code.
DO $$
DECLARE
temprow record ;
BEGIN
FOR temprow IN
SELECT o.objectid, o.nametag, cor.userseqno, cor.commseqno
FROM "commuserobjectrights" as cor
LEFT JOIN "object" as o ON cor.objectid = o.objectid
WHERE o.nametag LIKE 'commission.video_questions'
LOOP
INSERT INTO commuserobjectrights (objectid, commseqno, userseqno, access)
VALUES (temprow.objectid, temprow.commseqno, temprow.userseqno, TRUE);
END LOOP;
END;
$$;
This is not the most efficient way to do this task but for other tasks where you can't easily write SQL DO may be useful.

PostgreSQL ERROR: invalid input syntax for integer: "1e+06"

The full error message is:
ERROR: invalid input syntax for integer: "1e+06"
SQL state: 22P02
Context: In PL/R function sample
The query I'm using is:
WITH a as
(
SELECT a.tract_id_alias,
array_agg(a.pgid ORDER BY a.pgid) as pgids,
array_agg(a.sample_weight_geo ORDER BY a.pgid) as block_weights
FROM results_20161109.block_microdata_res_joined a
WHERE a.tract_id_alias in (66772, 66773, 66785, 66802, 66805, 66806, 66813)
AND a.bldg_count_res > 0
GROUP BY a.tract_id_alias
)
SELECT NULL::INTEGER agent_id,
a.tract_id_alias,
b.year,
unnest(shared.sample(a.pgids,
b.n_agents,
1 * b.year,
True,
a.block_weights)
) as pgid
FROM a
LEFT JOIN results_20161109.initial_agent_count_by_tract_res_11 b
ON a.tract_id_alias = b.tract_id_alias
ORDER BY b.year, a.tract_id_alias, pgid;
And the shared.sample function I'm using is:
CREATE OR REPLACE FUNCTION shared.sample(ids bigint[], size integer, seed integer DEFAULT 1, with_replacement boolean DEFAULT false, probabilities numeric[] DEFAULT NULL::numeric[])
RETURNS integer[] AS
$BODY$
set.seed(seed)
if (length(ids) == 1) {
s = rep(ids,size)
} else {
s = sample(ids,size, with_replacement,probabilities)
}
return(s)
$BODY$
LANGUAGE plr VOLATILE
COST 100;
ALTER FUNCTION shared.sample(bigint[], integer, integer, boolean, numeric[])
OWNER TO "server-superusers";
I'm pretty new to this stuff, so any help would be appreciated.
Not a problem of the function. Like the error messages says: The string '1e+06' cannot be cast to integer.
Obviously, the columns n_agents in your table results_20161109.initial_agent_count_by_tract_res_11 is not an integer column. Probably type text or varchar? (That info would help in your question.)
Either way, the assignment cast does not work for the target type integer. But it does for numeric:
Does not work:
SELECT '1e+06'::text::int; -- error as in question
Works:
SELECT '1e+06'::text::numeric::int;
If my assumptions hold, you can use this as stepping stone.
Replace b.n_agents in your query with b.n_agents::numeric::int.
It's your responsibility that numbers stay in integer range, or you get the next exception.
If that did not nail it, you need to look into function overloading:
Is there a way to disable function overloading in Postgres
And function type resolution:
PostgreSQL function call
The schema search path is relevant in many related cases, but you did schema-qualify all objects, so we can rule that out.
How does the search_path influence identifier resolution and the "current schema"
Your query generally looks good. I had a look and only found minor improvements:
SELECT NULL::int AS agent_id -- never omit the AS keyword for column alias
, a.tract_id_alias
, b.year
, s.pgid
FROM (
SELECT tract_id_alias
, array_agg(pgid) AS pgids
, array_agg(sample_weight_geo) AS block_weights
FROM ( -- use a subquery, cheaper than CTE
SELECT tract_id_alias
, pgid
, sample_weight_geo
FROM results_20161109.block_microdata_res_joined
WHERE tract_id_alias IN (66772, 66773, 66785, 66802, 66805, 66806, 66813)
AND bldg_count_res > 0
ORDER BY pgid -- sort once in a subquery. cheaper.
) sub
GROUP BY 1
) a
LEFT JOIN results_20161109.initial_agent_count_by_tract_res_11 b USING (tract_id_alias)
LEFT JOIN LATERAL
unnest(shared.sample(a.pgids
, b.n_agents
, b.year -- why "1 * b.year"?
, true
, a.block_weights)) s(pgid) ON true
ORDER BY b.year, a.tract_id_alias, s.pgid;

Using a UDF multiple times in a Sproc

I have a SQL 2k5 sproc I'm working with.
I need to reference a UDF to calculate price based on a few variables and the users permissions. I originally tried this, but it didn't work because I wasn't referencing a field...
SELECT dbo.f_GetPrice(model,userid,authType) 'YourPrice', name, description
FROM tblRL_Products
WHERE 'YourPrice' Between #fromPrice AND #toPrice
OR 'YourPrice' IS NULL
So I modified this to
SELECT dbo.f_GetPrice(model,userid,authType) 'YourPrice', name, description
FROM tblRL_Products
WHERE dbo.f_GetPrice(model,userid,authType) Between #fromPrice AND #toPrice
OR dbo.f_GetPrice(model,userid,authType) IS NULL
When SQL executes this sproc, is it running the function 3X's for each record or does it run it the one time and use the values in the other two places per row.
Is there a more efficient way of doing this?
Edit
This is the Scalar UDF. It needs to grab a price based on the type the user is authorized for, then once we have the right price we need to do a calculation on it. This is all stored in the authorization tables. Every user has an authorization for each line of products. So they may have different price types and calculations for each line, returning back dozens or even hundreds of lines in a single search result call.
In the above code I used authType, that was an old call, we don't use that parameter anymore.
ALTER function [dbo].[f_GetPrice]
(
#model uniqueidentifier,
#userID uniqueidentifier
)
returns money
as
begin
Declare #yourPrice money
WITH ProductPrice AS(
SELECT (CASE PriceType
WHEN 'msrp' THEN p.price_msrp
WHEN 'jobber' THEN p.price_jobber
WHEN 'warehouse' THEN p.price_warehouse
WHEN 'margin' THEN p.price_margin
WHEN 'mycost' THEN p.price_mycost
WHEN 'customprice1' THEN p.price_custom1
WHEN 'customprice2' THEN p.price_custom2
WHEN 'customprice3' THEN p.price_custom2
ELSE p.price_msrp
END) as YourPrice, aup.calc, aup.amount
FROM products p
JOIN lines l ON l.lineID=l.lineID
JOIN authorizations a ON l.authlineID=a.authlineID
JOIN authorizationusers au ON a.auID=au.auID
JOIN authorizationuserprices aup ON au.aupID=aup.aupID
WHERE au.userID=#userID AND p.modelID=#model)
SELECT #yourPrice=(CASE calc
WHEN 'amount' THEN YourPrice+amount
WHEN 'percent' THEN YourPrice+(YourPrice*amount/100)
WHEN 'divide' THEN YourPrice/amount
WHEN 'factore' THEN YourPrice*amount
WHEN 'none' THEN YourPrice
ELSE YourPrice
END) FROM ProductPrice
return #yourPrice
END
If you must use a udf for this, then use a subquery and filter outside the subquery:
select YourPrice, name, description
from
(
SELECT dbo.f_GetPrice(model,userid,authType) YourPrice, name, description
FROM tblRL_Products
) d
WHERE YourPrice Between #fromPrice AND #toPrice
OR YourPrice IS NULL
Then you are only calling your udf once instead of 3 times.
Scalar functions are not good, when they have to be applied to a quite number of rows. In this case definitely you can convert your scalar function into table-valued function, which will not be called one time for every row of input data.
create function [dbo].[ft_GetPrice]
(
#model uniqueidentifier,
#userID uniqueidentifier
)
returns table
as return
(
WITH ProductPrice AS (
SELECT (CASE PriceType
WHEN 'msrp' THEN p.price_msrp
WHEN 'jobber' THEN p.price_jobber
WHEN 'warehouse' THEN p.price_warehouse
WHEN 'margin' THEN p.price_margin
WHEN 'mycost' THEN p.price_mycost
WHEN 'customprice1' THEN p.price_custom1
WHEN 'customprice2' THEN p.price_custom2
WHEN 'customprice3' THEN p.price_custom2
ELSE p.price_msrp
END) as YourPrice, aup.calc, aup.amount
FROM products p
JOIN lines l ON l.lineID=l.lineID
JOIN authorizations a ON l.authlineID=a.authlineID
JOIN authorizationusers au ON a.auID=au.auID
JOIN authorizationuserprices aup ON au.aupID=aup.aupID
WHERE au.userID=#userID AND p.modelID=#model
)
SELECT
CASE calc
WHEN 'amount' THEN YourPrice+amount
WHEN 'percent' THEN YourPrice+(YourPrice*amount/100)
WHEN 'divide' THEN YourPrice/amount
WHEN 'factore' THEN YourPrice*amount
WHEN 'none' THEN YourPrice
ELSE YourPrice
END as YourPrice
FROM ProductPrice
)
GO
Which can be used then as:
SELECT fp.YourPrice, name, description
FROM tblRL_Products
outer apply dbo.ft_GetPrice(model, userid, authType) fp
WHERE fp.YourPrice Between #fromPrice AND #toPrice OR fp.YourPrice IS NULL

tsql : Access query to TSQL union update conversion correct?

I have a query in access which i need to convert to a stored proc in sql server 2005.
the query in access is as follows:
UPDATE
tblitem,
tblFileSignature
SET
tblitem.strFileProcesstype = [tblFileSignature].[STRFILEPROCESSTYPE], tblitem.strFileSignatureType = [tblFileSignature].[strfilesignaturetype]
WHERE
(((tblitem.strFileSignatureType) Is Null) AND
((tblitem.strFileExclude)="n") AND
((InStr([tblitem].[strfilesignature],[tblFileSignature].[strsignature]))=1) AND ((tblitem.uidItemType)=1 Or (tblitem.uidItemType)=5) AND
((tblitem.uidCollection)=[forms]![frmSetup]![txtInputCol]) AND ((tblitem.strFileSignature) Not Like "d0c*") AND
((tblFileSignature.strFileProcessType) Not Like "ZIP"));
in tsql.. would this be the same?
update tblItem
set
i.strFileProcesstype = f.strFileProcesstype,
i.strFileSignatureType = f.strfilesignaturetype
from tblItem as I UNION tblFileSignature as F
WHERE (((i.strFileSignatureType) Is Null) AND
((i.strFileExclude)="n") AND
((i.[strfilesignature] like F.strsignature)) AND
((i.uidItemType)=1 Or
(i.uidItemType)=5) AND
((i.uidCollection)=#inputcolumn AND
((i.strFileSignature) Not Like 'd0c%') AND
((F.strFileProcessType) Not Like 'ZIP'));
thanks in advance
UPDATE:
so i'm going with the following. if i uncomment the declare and select clause and just execute from the declare down, it runs, if i comment the declare and select parts, it says error near ';'.
UPDATE I
SET
I.strFileProcesstype = F.STRFILEPROCESSTYPE,
I.strFileSignatureType = F.strfilesignaturetype
--declare #uidcollectionID int
--select I.strFileSignatureType
from
tblItem I
inner join tblFileSignature F
on
I.strfilesignature = left(F.strsignature,len(I.strfilesignature))
WHERE I.strFileSignatureType Is Null
AND I.strFileExclude='n'
AND I.uidItemType in (1,5)
AND I.uidCollection = #uidCollectionID
AND left(I.strFileSignature,3) <> 'd0c'
AND F.strFileProcessType <> 'ZIP';
any ideas?
You should change the
Double Quotes to Single Quotes
* to %
Replace the InStr with LIKE
Other than that, it looks fine to me.
No, you'd use a JOIN, not a UNION.
You can either make it a CROSS JOIN, and continue to apply the join conditions in the WHERE clause, or you can make it an inner join:
from tblItem as I INNER JOIN tblFileSignature as F
ON ((InStr(i.[strfilesignature],F.[strsignature]))=1)
And remove that condition from the WHERE clause (Lieven's answer also applies).
This should be close to what you need. May need to work on the join condition, but I think my conversion from INSTR will do it.
UPDATE i
SET strFileProcesstype = fs.STRFILEPROCESSTYPE,
strFileSignatureType = fs.strfilesignaturetype
FROM tblitem i
INNER JOIN tblFileSignature fs
ON i.strfilesignature = LEFT(fs.strsignature, LEN(i.strfilesignature))
WHERE i.strFileSignatureType IS Null
AND i.strFileExclude='n'
AND i.uidItemType IN (1,5)
AND i.uidCollection = #inputcolumn
AND LEFT(i.strFileSignature,3) <> 'd0c'
AND fs.strFileProcessType <> 'ZIP';

T-SQL Delete command basing on table variable

I need to delete some rows from table where indexes are equal indexes in table variable
declare #m_table as table
(
number NUMERIC(18,0)
)
...
inserting some rows into #m_table
...
DELETE ct FROM [dbo].[customer_task] ct
inner join project_customer pc on pc.id_customer = #m_table.number
inner join customer_user cu on cu.id_project_customer = pc.id
WHERE ct.id_csr_user = cu.id AND ct.id_status = 1;
but this code generates an error: Must declare the scalar variable "#m_table" How to solve that ?
You probably have a 'GO' (a batch separator) in those '...'
Variable declarations do not span batches.
The error means that SQL is expecting you to treat #m_table like a standard table, rather than a scalar (int, bit, etc.) variable. Perhaps something like this will work?
DELETE ct FROM [dbo].[customer_task] ct
WHERE ct.id_csr_user IN (
SELECT cu.id FROM customer_user cu
INNER JOIN project_customer pc ON pc.id = cu.id_project_customer
WHERE pc.id_customer IN (SELECT number FROM #m_table.number)
) AND ct.id_status = 1;