My T-SQL code is:
CREATE PROCEDURE [dbo].[test_mssql]
(#TaskId int = NULL,
#RootTaskId int = NULL,
#SessionUserId int,
#XmlParam xml = NULL,
#OnlyOneRow bit = 0,
#DataType varchar (20) = 'GetChilds')
AS
BEGIN
CREATE TABLE #t (PID int, Id int, UN varchar(500))
INSERT INTO #t (PID, Id, UN)
SELECT osu.PID, osu.Id, u.FN
FROM Users u WITH (NOLOCK)
JOIN uosu WITH (NOLOCK) ON u.UID = uosu.UID
JOIN osu WITH (NOLOCK) ON uosu.OSUID = osu.Id
WHERE u.IsFired_2 = 0
AND uosu.IsPrimary = 1
BEGIN
SELECT PID, Id, UN
FROM #t
END
END
I tried to rewrite it to plpgsql:
CREATE OR REPLACE FUNCTION test_plpgsql(
IN TaskId integer = null,
IN RootTaskId integer = null,
IN SessionUserId integer = null,
IN XmlParam xml = null,
IN OnlyOneRow boolean = false,
IN DataType varchar(20) = 'GetChilds'
) RETURNS TABLE (PID integer, Id integer, UN varchar(500))
AS $$
BEGIN
CREATE TEMPORARY TABLE t (PID integer, Id integer, UN varchar(500));
INSERT INTO t (PID, Id, UN)
SELECT osu.PID, osu.Id, u.FN
FROM Users u
JOIN uosu ON u.UID = uosu.UID
JOIN osu ON uosu.OSUID = osu.Id
WHERE u.IsFired_2 = 0
AND uosu.IsPrimary = 1;
RETURN QUERY SELECT PID, Id, UN FROM t;
END;
$$ LANGUAGE plpgsql;
But I have an error:
POSITION: 178; SQL: ;
---> Npgsql.PostgresException: 42601: syntax error at or near "$1"
Is the error in the plpgsql script I wrote, or in the Npgsql data provider?
Documentation is your friend :-). More in this case, because PL/pgSQL is very different than T-SQL. Good start is forgot all what you know about T-SQL. On second hand - stored procedures in Postgres (PL/pgSQL) are very similar to Oracle (PL/SQL). PL/pgSQL is almost simplified version of PL/SQL (although some part are very different still).
You should to use RETURN QUERY command directly:
CREATE OR REPLACE FUNCTION test_plpgsql(
IN TaskId integer = null,
IN RootTaskId integer = null,
IN SessionUserId integer = null,
IN XmlParam xml = null,
IN OnlyOneRow boolean = false,
IN DataType varchar(20) = 'GetChilds'
) RETURNS TABLE (PID integer, Id integer, UN varchar(500))
AS $$
BEGIN
RETURN QUERY
SELECT osu.PID, osu.Id, u.FN
FROM Users u
JOIN uosu ON u.UID = uosu.UID
JOIN osu ON uosu.OSUID = osu.Id
WHERE u.IsFired_2 = 0
AND uosu.IsPrimary = 1;
END;
$$ LANGUAGE plpgsql;
Maybe you use some very old version of PostgreSQL. In these versions, the PL/pgSQL identifier was preferred always and everywhere. Clause `RETURNS TABLE(PID integer, ..) defines an structure of output relation (columns of output relation), but it defines local variables too (one for any output column).
The statement CREATE TEMPORARY TABLE t (PID integer, Id integer, UN varchar(500)); uses identifier PID. Next statement INSERT INTO t (PID, Id, UN) uses identifier PID too. Old versions (before PostgreSQL 9.0 (2010)) replaces identifies to arguments symbols (like $1) everywhere (without knowledge of context). Modern versions of Postgres (last 12 years) use different mechanism, and there should not be this problem. I was able to create function from your example without any problems.
Related
The query below has a scalar variable #LocationID that can either be a varchar or a uniqueidentifier depending on the value of the scalar variable #LimitTo.
This query worked before the addition of the addition of the lines
OR ((#LimitTo = 'Drawing') AND (ba.DrawingGuid = ...
DrawingGuid is a Guid and not text like the other OR statements.
This suggests that the SQL is analyzed and selects a single conversion method ahead of running the query, and seeing that there are two possibilities, throws the following error when I use #LocationID as a Varchar (it works fine if #LocationID is a uniqueidentifier)
Conversion failed when converting from a character string to uniqueidentifier
Though, I'm not sure if this theory is correct. Is there a way to have the #LocationID variable either be a varchar or uniqueidentifier for this query?
Here's the query:
DECLARE #Contract VARCHAR(60);
SET #Contract = 'F8C018CA-A00C-4BB1-B920-D460786F6820';
DECLARE #LimitTo VARCHAR(30);
SET #LimitTo = 'WorkZone';--'Drawing'; 'WorkZone'
DECLARE #LocationID VARCHAR(60);
SET #LocationID = 'North'; --'2FB87868-D5D7-4A84-916F-F1DEE871A085'; 'North'
SELECT DISTINCT
asm.AssemblyCode,
asm.AssemblyRestorationDesc,
asm.AssemblyUnit,
asm.AssemblyGuid,
(SELECT SUM(m.MarkerQuantity)
FROM Marker m
WHERE m.AssemblyGuid = asm.AssemblyGuid
AND m.MarkerExcludeFromScope = 'False'
AND m.ContractGuid = #Contract
AND (((#LimitTo = 'WorkZone') AND (m.MarkerWorkZone = #LocationID))
OR ((#LimitTo = 'WorkRegion') AND (m.MarkerWorkRegion = #LocationID))
OR ((#LimitTo = 'Drawing') AND (m.DrawingGuid = #LocationID)))
AND m.Deleted = 0) AS Quantity,
(SELECT SUM(bm.MarkerQuantity)
FROM BaselineMarker bm
WHERE bm.AssemblyCode = asm.AssemblyCode
AND bm.MarkerExcludeFromScope = 'False'
AND (((#LimitTo = 'WorkZone') AND (bm.MarkerWorkZone = #LocationID))
OR ((#LimitTo = 'WorkRegion') AND (bm.MarkerWorkRegion = #LocationID))
OR ((#LimitTo = 'Drawing') AND (bm.DrawingGuid = #LocationID)))
AND bm.Deleted = 0) AS BaselineQuantity,
(SELECT SUM(ba.AllowanceQuantity)
FROM BaselineAllowance ba
WHERE ba.AssemblyCode = asm.AssemblyCode
AND (((#LimitTo = 'WorkZone') AND (ba.AllowanceWorkZone = #LocationID))
OR ((#LimitTo = 'WorkRegion') AND (ba.AllowanceWorkRegion = #LocationID))
OR ((#LimitTo = 'Drawing') AND (ba.DrawingGuid = CONVERT(uniqueidentifier, #LocationID))))
AND ba.Deleted = 0) AS AllowanceQuantity
FROM
Assembly asm
WHERE
asm.Deleted = 0
ORDER BY
asm.AssemblyCode, asm.AssemblyRestorationDesc,
asm.AssemblyUnit, asm.AssemblyGuid
I think I understand what you are trying to do.
You are trying to compare different columns to the #locationId, and the column you want to compare depends on the value of #limitTo. The other columns (like AllowanceWorkZone) are presumably all varchar, but in cases where #limitTo = 'Drawing' you instead want to compare #LocationId with the DrawingGuid column, which is a uniqueidentifier.
No, you can't do this.
Just declare a second variable which is a uniqueidentifier, and use that in the comparison against the DrawingGuid column...
declare #LocationVarchar varchar(36) = 'North';
declare #LocationGuid uniqueidentifier = try_cast(#LocationVarchar as uniqueidentifier);
declare #LimitTo varchar(30) = 'foo';
select ...
from ...
where (#LimitTo = 'foo' and MyVarcharColumn = #locationVarchar)
or (#LimitTo = 'bar' and MyGuidColumn = #locationGuid);
In the above code, if the incoming filter value is not a valid uniqueidentifier, then #locationGuid will be null, but it won't matter, because the comparison of MyVarcharColumn against #LocationVarchar is the relevant predicate.
You could of course also use try_cast directly in the predicate, but I separated it out to a separate variable just to make it clearer what was going on.
Edit: Without the second variable, it would be...
where (#limitTo = 'foo' and MyVarcharColumn = #locationVarchar)
or (#limitTo = 'bar' and MyGuidColumn = try_cast(#locationVarchar as uniqueidentifier);
I wanted to check for null or empty GUID in stored procedure. I have used the below code:
CREATE PROCEDURE [dbo].[GetGuid]
#buildingID XML,
AS
DECLARE #fetchedBuildValue uniqueidentifier
SET #fetchedBuildValue = (SELECT data.item.value('./#ID', 'uniqueidentifier') AS PId
FROM #buildingID.nodes('/items/item') data(item));
SELECT
DemoUser.[ID],
DemoUser.[Suffix],
DemoUser.[BusinessPhoneNumber]
INNER JOIN
Relationship AS T_U ON T_U.Target = DemoUser.ID
AND T_U.TargetType = 0
AND T_U.SourceType = 6
INNER JOIN
DemoTenant ON T_U.Source = DemoTenant.ID
WHERE
(#firstname IS NULL OR DemoUser.FirstName LIKE '%' + #firstname + '%')
AND ((#fetchedBuildValue = '00000000-0000-0000-0000-000000000000'
AND Building.State = #state
AND Building.City = #city)
OR
(#fetchedBuildValue != '00000000-0000-0000-0000-000000000000'
AND Building.ID = #fetchedBuildValue))
The above stored procedure is not checking for empty guid. The guid that I am passing from code is in a xml as below:
<items><item ID="00000000-0000-0000-0000-000000000000" /></items>
However, if a valid guid is passed, it is working fine.
How can I add an empty check here so that when the guid is empty, my state and city where clause work instead of the guild column
I have 3 tables in SQL Server that are exactly the same. I am trying to write a stored procedure to get the value of 2 columns from 2 tables and update the 3rd table with the difference. These are the 3 tables:
Current
Proposed
Final
I am passing in rfds_processing_id. It is similar to an order number and contains multiple rows, so matching has to be done on rfds_processing_id, type, sector, and position.
I need to match every row from the 3 tables and then subtract the value of Current.qty from Final.qty and update the Proposed.qty with the difference. It also needs to update Proposed.model with the value of Final.model (when Proposed.qty > 0). Currently it is setting every row to the same value. I'm assuming I need to loop through the rows. What should that look like?
ALTER PROCEDURE [dbo].[CalculateProposedAntennas]
(
#rfds_processing_id uniqueidentifier = null,
#id uniqueidentifier OUTPUT
)
AS
BEGIN
SET NOCOUNT ON
UPDATE P
SET qty = (F.qty - C.qty) , model = F.model
FROM Proposed_Antenna P
INNER JOIN Final_Antenna F
ON P.rfds_processing_id = F.rfds_processing_id
INNER JOIN Current_Antenna C
ON F.rfds_processing_id = C.rfds_processing_id
WHERE
F.rfds_processing_id = C.rfds_processing_id
AND F.sector = C.sector
AND F.type = C.type
AND F.position = C.position
RETURN
END
The following query should do what you want:
ALTER PROCEDURE [dbo].[CalculateProposedAntennas]
(
#rfds_processing_id uniqueidentifier = null,
#id uniqueidentifier OUTPUT
)
AS
BEGIN
SET NOCOUNT ON
UPDATE P
SET P.qty = F.qty - C.qty
,P.model = CASE WHEN Proposed.qty > 0 THEN F.model ELSE NULL END
FROM Proposed_Antenna P
INNER JOIN Final_Antenna F
ON F.rfds_processing_id = P.rfds_processing_id AND F.sector = P.sector AND F.type = P.type AND F.position = P.position
INNER JOIN Current_Antenna C
ON C.rfds_processing_id = P.rfds_processing_id AND C.sector = P.sector AND C.type = P.type AND C.position = P.position
RETURN
END
I highly recommend you to create a [UID] column in all three of your tables - the unique identifier case can be generated as below, the generated key is of varbinary type which is far better than string comparison in joins
select [UID] = HashBytes('MD5',rfds_processing_id+type+sector+position)
I've been using this method if I need to do different actions in a stored procedure that isn't update insert or delete. Mostly if I have to do a select to show different information depending on the person role. I was told this is very bad practice and bad performance but I don't see how can I do this without having to write a stored procedure for each command and that looks even worse because I have so many commands. My question is, is there a better way to do this type of thing in SQL Server?
DROP PROCEDURE IF EXISTS spRequisicao
GO
CREATE PROCEDURE spRequisicao
-- hRequisicao
#Action NVARCHAR(20),
#MotivoCriacao NVARCHAR(100) = NULL,
#IdCodeRequest INT = NULL,
#Projecto NVARCHAR(50) = NULL,
#Desenho NVARCHAR(50) = NULL,
#Indice NVARCHAR(20) = NULL,
#CadenciaMensal INT = NULL,
#NumCOMDEV NVARCHAR(50) = NULL,
#RefCliente NVARCHAR(50) = NULL,
#RefInterna NVARCHAR(7) = NULL,
#QTDLancamentoFormas INT = NULL,
#CapacidadeReal NVARCHAR(50) = NULL,
#Peso FLOAT = NULL,
#IdCaixa INT = NULL,
#IdComp INT = NULL,
#IdMetProd INT = NULL,
#IdColaborador INT = NULL,
#IdStatus INT = NULL,
#IdInfoLogistica bit = NULL,
-- hDataLog
#IdData INT = NULL,
#DataAbertura DATETIME = NULL,
#DataAlteracao DATETIME = NULL,
#IdReq INT = NULL,
-- hCaixa
#TipoCaixa NVARCHAR(50) = NULL,
#QTDPecasCaixa INT = NULL,
#UnidadeProducao NVARCHAR(20) = NULL,
#CelulaProducao NVARCHAR(50) = NULL,
#NumKanbansProducao INT = NULL,
-- hMetodologiaProducao
#TipoMetodologia NCHAR(3) = NULL,
#QTDMetProd INT = NULL,
-- hComponentes
#QTDComp INT = NULL,
AS
BEGIN
SET NOCOUNT ON;
IF #Action = 'SELECTALL'
BEGIN
SELECT
hr.IdColaborador, hr.MotivoCriacao, hr.IdReq,
Nome, hcb.Email, hr.RefInterna, hi.Descricao AS InfoLog,
hs.Descricao AS STATUS, hd.DataAbertura AS DataCriacao
FROM
hRequisicao AS hr
JOIN
hColaborador AS hcb ON hcb.IdColaborador = hr.IdColaborador
JOIN
hStatus AS hs ON hs.IdStatus = hr.IdStatus
JOIN
hInfoLogistica hi ON hi.IdInfoLogistica = hr.IdInfoLogistica
JOIN
hDataLog hd ON hd.IdReq = hr.IdReq
WHERE
hr.IdStatus != 6
END
IF #Action = 'SELECTALL_USERID'
BEGIN
SELECT
hr.IdColaborador, hr.IdReq, Nome, hcb.Email, hr.RefInterna,
hs.Descricao AS STATUS, hl.DataAbertura AS DataCriacao, hi.Descricao AS InfoLog
FROM
hRequisicao AS hr
JOIN
hColaborador AS hcb ON hcb.IdColaborador = hr.IdColaborador
JOIN
hStatus AS hs ON hs.IdStatus = hr.IdStatus
JOIN
hDataLog AS hl ON hl.IdReq = hr.IdReq
JOIN
hInfoLogistica AS hi ON hi.IdInfoLogistica = hr.IdInfoLogistica
WHERE
hr.IdColaborador = #IdColaborador AND hr.IdStatus != 6
END
IF #Action = 'SELECT'
BEGIN
SELECT
IdReq, hr.MotivoCriacao, hr.IdCodeRequest, hr.IdColaborador,
Nome, hcb.Email, Projecto, Desenho, Indice, CadenciaMensal,
NumCOMDEV, RefCliente, RefInterna, QTDLancamentoFormas,
CapacidadeReal, Peso, TipoCaixa, QTDPecasCaixa, UnidadeProducao,
CelulaProducao, NumKanbansProducao, QTDComp,
TipoMetodologia, QTDMetProd, hi.Descricao AS InfoLog
FROM
hRequisicao AS hr
JOIN
hCodeRequest AS hcr ON hr.IdCodeRequest = hcr.IdCodeRequest
JOIN
hCaixa AS hc ON hr.IdCaixa = hc.IdCaixa
JOIN
hComponentes AS hcp ON hr.IdComp = hcp.IdComp
JOIN
hMetodologiaProducao AS hmp ON hr.IdMetProd = hmp.IdMetProd
JOIN
hColaborador AS hcb ON hcb.IdColaborador = hr.IdColaborador
JOIN
hInfoLogistica AS hi ON hi.IdInfoLogistica = hr.IdInfoLogistica
WHERE
IdReq = #IdReq
END
IF #Action = 'CHECKREQ'
BEGIN
SELECT IdReq, IdStatus
FROM hRequisicao
WHERE IdColaborador = #IdColaborador AND IdStatus = 6
END
IF #Action = 'CHECKSTATUS'
BEGIN
SELECT Descricao
FROM hStatus hs
JOIN hRequisicao hr ON hr.IdStatus = hs.IdStatus
WHERE IdReq = #IdReq
END
END
I have been taught to understand that any arrangement (collection, selection, positioning etc.) of data is inherently information independent of the thing behing arranged.
The relevance of the above is that the columns and relationships represented by your various queries presumably critical to the understanding your database/application.
This means that you should be commenting and documenting them appropriately and likely separately as well.
I generally prefer to design databases (as best as I can) in a way that minimizes the amount of description necessary for future developers etc.
To do so I find that creating views based on queries such as those used in your example, naming them appropriately as well as naming the resulting columns and columns appropriate for where criteria appropriately very intuitive and easy to maintain.
In terms of performance I can't confidently advise or inform either way although I assume that it will be more influenced by the database design than sproc VS view.
conclusion: there is no 'better' way to accomplish this task independent of the broader contextual design strategy and requirements
Due to programming language restraints, my ERP system does not allow me to make advanced select queries, that´s why I need to rely on making a stored procedure on SQL Server, calling it from the ERP system and getting the result through an array.
The code belows works ok, but I think it´s not the correct way to assign the values to the output variables... I wanted to assign the output variables directly from the select, without need to make a #temp table... is it possible? or did I make it right?
If the code can be enhanced, I would gracefully accept any suggestions. The objective of the code is call a stored procedure with a RFID tag (read by a RFID card reader) and then get some employee info from another database, from another ERP, on another server (linked through SQL "linked servers")
ALTER procedure [dbo].[KSBValTag]
(
#rfid varchar(20),
#OUT_NUMCAD varchar(10) OUTPUT,
#OUT_NOMFUN varchar(50) OUTPUT,
#OUT_SIT varchar(2) OUTPUT,
#OUT_CODCCU varchar(5) OUTPUT,
#OUT_NOMCCU varchar(30) OUTPUT
) as
Begin
set #rfid = SUBSTRING(#rfid, PATINDEX('%[^0]%', #rfid+'.'), LEN(#rfid))
select fun.numcad as Numcad,
fun.nomfun as Nomefun,
fun.sitafa as Situacao,
fun.codccu as CodCCU,
ccu.nomccu as NomeCCU
into #temp
from [vetorh].vetorh.r034fun as FUN
inner join
[vetorh].vetorh.r018ccu CCU
on fun.codccu = ccu.codccu
where numcad = (select num_cartao from [ksb-app01].topacesso.dbo.Cartoes where CodigoDeBarras = #rfid)
and tipcol = '1'
set #OUT_NUMCAD = (select Numcad from #temp)
set #OUT_NOMFUN = (select Nomefun from #temp)
set #OUT_SIT = (select Situacao from #temp)
set #OUT_CODCCU = (select CodCCU from #temp)
set #OUT_NOMCCU = (select NomeCCU from #temp)
End
select
#OUT_NUMCAD = fun.numcad,
#OUT_NOMFUN = fun.nomfun,
#OUT_SIT = fun.sitafa,
#OUT_CODCCU = fun.codccu,
#OUT_NOMCCU = ccu.nomccu
from [vetorh].vetorh.r034fun as FUN
inner join
[vetorh].vetorh.r018ccu CCU
on fun.codccu = ccu.codccu
where numcad = (select num_cartao from [ksb-app01].topacesso.dbo.Cartoes where CodigoDeBarras = #rfid)
and tipcol = '1'