problem with multi statement table valued function, what am I doing wrong? - sql-server

I having problems with this function, seems like #idUsuario and #passCorrecto aren't getting any value, so, when I use this variables in the where clause I'm not getting any result data.
ALTER FUNCTION [dbo].[login](#usuario varchar(20), #password varchar(20))
RETURNS #info TABLE (nombre varchar(70) not null, tipo varchar(30) not null)
AS
BEGIN
DECLARE #idUsuario int = dbo.usuarioExiste(#usuario)
DECLARE #passCorrecto bit = dbo.passwordCorrecto(#idUsuario, #password)
INSERT #info
SELECT
usuarios.nombreUsuario, tiposUsuarios.tipoUsuario
FROM
usuarios
LEFT JOIN
tiposUsuarios
ON
usuarios.idTipoUsuario = tiposUsuarios.idTipoUsuario
WHERE
usuarios.idUsuario = #idUsuario and
usuarios.estatus = 'ACTIVO' and
#passCorrecto = 1
RETURN
END
What am I doing wrong?
EDIT
Here are the function used above:
ALTER FUNCTION [dbo].[usuarioExiste]
(
#usuario varchar(20)
)
RETURNS integer
AS
BEGIN
DECLARE #idUsuario integer
SELECT
#idUsuario = idUsuario
FROM
usuarios
WHERE
usuario = #usuario
if #idUsuario is null begin
set #idUsuario = 0
end
RETURN #idUsuario
END
ALTER FUNCTION [dbo].[passwordCorrecto]
(
#usuario varchar(20),
#password varchar(20)
)
RETURNS bit
AS
BEGIN
DECLARE #esCorrecto bit
SELECT
#esCorrecto = case when password = #password then 1 else 0 end
FROM
usuarios
WHERE
usuario = #usuario
RETURN #esCorrecto
END
EDIT 2
As suggested by Beth, I created new functions that returns the values that I need like this:
CREATE FUNCTION [dbo].[usuarioExisteTest]
(
#usuario varchar(20)
)
RETURNS int
AS
BEGIN
declare #idUsuario int;
set #idUsuario = 1;
return (#idUsuario);
END;
By doing this I'm getting the data I need, am I setting the values to return the wrong way in the original functions?
DECLARE #idUsuario integer
SELECT
#idUsuario = idUsuario
FROM
usuarios
WHERE
usuario = #usuario

I know SQL Server 2008 supports the combined DECLARE/assign, but have you tried separating them?
DECLARE #idUsuario int
SET idUsuario = dbo.usuarioExiste(#usuario)
DECLARE #passCorrecto bit
SET passCorrecto = dbo.passwordCorrecto(#idUsuario, #password)
From BOL
Assigns a value to the variable
in-line. The value can be a constant
or an expression, but it must either
match the variable declaration type or
be implicitly convertible to that
type.
Of course, a udf is an expression, but is it possible there is some anomaly when used in DECLARE (like when you use a udf in CHECK constraint: it's not reliable). Or DECLARE ins a udf for some reason.
Please humour me. Try it.
Also, you have no "failsafe" inside dbo.passwordCorrecto which means it could return NULL which will always evaluate to false in the outer udf.

try printing the values of the variables after you declare them. If they aren't set correctly, try calling the functions in a new query window instead of within [dbo].[login]. That should help identify the problem.

I've found a solution, seem like using select #var = some_value doesn't work well. See this:
A SELECT statement that contains a variable assignment cannot be used to also perform typical result set retrieval operations.
These are the fixed functions:
usuarioExiste
CREATE FUNCTION [dbo].[usuarioExiste]
(
#usuario varchar(20)
)
RETURNS int
AS
BEGIN
declare #idUsuario int = 0;
set #idUsuario = (
select
idUsuario
from
usuarios
where
usuario = #usuario);
if #idUsuario is null begin
set #idUsuario = 0;
end;
return (#idUsuario);
END;
passwordCorrecto
CREATE FUNCTION [dbo].[passwordCorrecto]
(
#idUsuario int,
#password varchar(20)
)
RETURNS bit
AS
BEGIN
declare #esCorrecto bit = 'false';
set #esCorrecto = (
select
case when password = #password then 'true' else 'false' end
from
usuarios
where
usuarios.idUsuario = #idUsuario);
return (#esCorrecto);
END;
and login
CREATE FUNCTION [dbo].[login](#usuario varchar(20), #password varchar(20))
RETURNS #info TABLE (nombre varchar(70) not null, tipo varchar(30) not null)
AS
BEGIN
DECLARE #idUsuario int = dbo.usuarioExistetest(#usuario);
DECLARE #passCorrecto bit = dbo.passwordCorrectotest(#idUsuario, #password);
INSERT #info
SELECT
usuarios.nombreUsuario, tiposUsuarios.tipoUsuario
FROM
usuarios
LEFT JOIN
tiposUsuarios
ON
usuarios.idTipoUsuario = tiposUsuarios.idTipoUsuario
WHERE
usuarios.idUsuario = #idUsuario and
usuarios.estatus = 'ACTIVO' and
#passCorrecto = 'true';
RETURN
END;

Related

How to iterate over a string of varying length, replacing different abbreviations with their full text. All abbreviations separated by a semicolon

My problem is this; I have a field in a table that contains values like this:
NP
NP;MC;PE
MC;AB;AT;MI;TC;WM
OS
OG
I want to convert these abbreviations to their full name. i.e. NP becomes Nuclear Power, OG becomes Oil and Gas, MI becomes Military etc.
My desired output would be:
Nuclear Power
Nuclear Power;Military;Pesticides
and so on.
I'm creating this as a function. I got it working for just the one abbreviation and then the same for two. However my issue is that I may have 5 abbreviations or 7. I know my current approach is dreadful but cannot figure out how to loop it in the right way.
Please note: I've shortened the list of abbreviations for StackOverflow but there's 25 in total.
Please further note: I did the function bottom up (I don't know why) and got the two value and single value working. I've removed anything I did for values over 3 as nothing I did worked.
ALTER FUNCTION [dbo].[get_str_full]
(
-- Add the parameters for the function here
#str_input VARCHAR(250)
)
RETURNS VARCHAR(250)
AS
BEGIN
-- Declare the return variable here
DECLARE #Result VARCHAR(250)
DECLARE #TEMPSTRING VARCHAR(250)
DECLARE #TEMPSTRING_RIGHT AS VARCHAR(250)
-- DECLARE #PI_COUNT BIGINT
DECLARE #COUNTER INT
DECLARE #TOTAL_VALS BIGINT
DECLARE #STRING_ST VARCHAR(250)
DECLARE #POS_STR BIGINT
DECLARE #REMAINING_STR VARCHAR(250)
-- Used for easy loop skips
DECLARE #LEFTSKIP AS BIGINT
SET #LEFTSKIP = 1
SET #Result = #str_input
SET #STRING_ST = #Result
SET #COUNTER = (LEN(#Result) - LEN(REPLACE(#Result,';',''))) + 1
SET #TOTAL_VALS = (LEN(#Result) - LEN(REPLACE(#Result,';',''))) + 1
-- If the string has a semicolon then there's more than one PI value
IF CHARINDEX(';', #Result) > 0
BEGIN
WHILE #COUNTER > 0
BEGIN
IF #TOTAL_VALS >= 3 -- If counter is more than 2 then there's three or more
BEGIN
DECLARE #TEMP_VAL BIGINT
SET #TEMP_VAL = 5
END
ELSE IF #TOTAL_VALS = 2-- Theres 2
BEGIN
-- Do left two chars first
IF #LEFTSKIP = 1
BEGIN
SET #TEMPSTRING = LEFT(#Result, 2)
SELECT #TEMPSTRING = CASE #TEMPSTRING
WHEN 'MC' THEN 'Military Contracting'
WHEN 'NP' THEN 'Nuclear'
WHEN 'OG' THEN 'Oil & Gas'
WHEN 'OS' THEN 'Oil Sands'
WHEN 'PM' THEN 'Palm Oil'
WHEN 'PE' THEN 'Pesticides'
ELSE #TEMPSTRING
END
SET #LEFTSKIP = 2
END
ELSE IF #LEFTSKIP = 2
BEGIN
SET #TEMPSTRING_RIGHT = RIGHT(#Result, 2)
SELECT #TEMPSTRING_RIGHT = CASE #TEMPSTRING_RIGHT
WHEN 'MC' THEN 'Military Contracting'
WHEN 'NP' THEN 'Nuclear'
WHEN 'OG' THEN 'Oil & Gas'
WHEN 'OS' THEN 'Oil Sands'
WHEN 'PM' THEN 'Palm Oil'
WHEN 'PE' THEN 'Pesticides'
ELSE #TEMPSTRING_RIGHT
END
END
END
SET #COUNTER = #COUNTER - 1
END
SET #Result = CONCAT(#TEMPSTRING,';', #TEMPSTRING_RIGHT)
END
ELSE
BEGIN
SET #Result = REPLACE(#Result, 'MC', 'Military Contracting')
SET #Result = REPLACE(#RESULT, 'NP', 'Nuclear Power')
SET #Result = REPLACE(#Result, 'OG', 'Oil & Gas')
SET #Result = REPLACE(#Result, 'OS', 'Oil Sands')
SET #Result = REPLACE(#Result, 'PM', 'Palm Oil')
SET #Result = REPLACE(#Result, 'PE', 'Pesticides')
END
-- Return the result of the function
RETURN #Result
END
First for some easily consumable sample data:
DECLARE #tranlation TABLE(tCode VARCHAR(10), tString VARCHAR(40));
DECLARE #t TABLE(String VARCHAR(1000));
INSERT #t VALUES('PE;N'),('NP'),('NP;MC;PE;XX')
INSERT #tranlation VALUES ('N','Nukes'),('NP','Nuclear Power'),('MC','Military'),
('PE','Pesticides');
Note my updated sample data which includes "XX", which has no match , and an "N" for "Nukes" which would wreck any solution which leverages REPLACE. If you are on SQL 2016+ you can use STRING_SPLIT and STRING_AGG.
SELECT
OldString = t.String,
NewString = STRING_AGG(ISNULL(tx.tString,items.[value]),';')
FROM #t AS t
OUTER APPLY STRING_SPLIT(t.String,';') AS items
LEFT JOIN #tranlation AS tx
ON items.[value] = tx.tCode
GROUP BY t.String ;
Returns:
OldString NewString
----------------- -------------------------------------------
NP Nuclear Power
NP;MC;PE;XX Nuclear Power;Military;Pesticides;XX
PE;N Pesticides;Nukes
You should really fix your table design so that you do not store multiple pieces of info in one column.
If you would like it as a function, I would strongly recommend an inline Table-Valued function rather than a scalar function.
If you have SQL Server version 2017+ you can use STRING_SPLIT and STRING_AGG for this.
CREATE OR ALTER FUNCTION GetFullStr
( #str varchar(250) )
RETURNS TABLE
AS RETURN
(
SELECT STRING_AGG(ISNULL(v.FullStr, s.value), ';') result
FROM STRING_SPLIT(#str, ';') s
LEFT JOIN (VALUES
('MC', 'Military Contracting'),
('NP', 'Nuclear'),
('OG', 'Oil & Gas'),
('OS', 'Oil Sands'),
('PM', 'Palm Oil'),
('PE', 'Pesticides')
) v(Abbr, FullStr) ON v.Abbr = s.value
);
GO
You can, and should, replace the VALUES with a real table.
On 2016 you would need FOR XML PATH instead of STRING_AGG:
CREATE OR ALTER FUNCTION GetFullStr
( #str varchar(250) )
RETURNS TABLE
AS RETURN
(
SELECT STUFF(
(SELECT ';' + ISNULL(v.FullStr, s.value)
FROM STRING_SPLIT(#str, ';') s
LEFT JOIN (VALUES
('MC', 'Military Contracting'),
('NP', 'Nuclear'),
('OG', 'Oil & Gas'),
('OS', 'Oil Sands'),
('PM', 'Palm Oil'),
('PE', 'Pesticides')
) v(Abbr, FullStr) ON v.Abbr = s.value
FOR XML PATH(''), TYPE
).value('text()[1]','varchar(2500)'),
, 1, 1, '')
);
GO
You use it like this:
SELECT s.result AS FullStr
FROM table
OUTER APPLY GetFullStr(value) AS s;
-- alternatively
SELECT (SELECT * FROM GetFullStr(value)) AS FullStr
FROM table;
You could assign your abbreviation mappings to a TABLE variable and then use that for your REPLACE. You could build this into a function, then pass your string values in.
The test below returns Military:Nuclear Power:XX.
declare #mapping table (abbrev varchar(50), fullname varchar(100))
insert into #mapping(abbrev, fullname)
values ('NP','Nuclear Power'),
('MC','Military')
declare #testString varchar(100), #newString varchar(100)
set #teststring = 'MC:NP:XX'
set #newString = #testString
SELECT #newString = REPLACE(#newString, abbrev, fullname) FROM #mapping
select #newString

How to assign a variable to the execution of a stored procedure?

I want to assign the value returned from the sp_formCreateEventID stored procedure into a new variable (#eventId). I initially thought this was the way to go. This solution is also in line with the EXEC command generated by SSMS.
However, for some reason the EXEC line returns an INT from the stored procedure as expected, but when it cant' assigned it's value to the #eventId variable.
DECLARE #eventId INT
EXEC #eventId = sp_formCreateEventID #patientId, #programId, #clinicianId, #formId, #phaseTypeId, #draft, #dataCollectionDate, NULL
SELECT #eventId
sp_formCreateEventID (don't hate me, I didn't write this...):
ALTER PROCEDURE [dbo].[sp_formCreateEventID]
#PatientID int,
#ProgramID int,
#ClinicianID int,
#FormID int,
#PhaseTypeID int,
#Draft varchar(5),
#CompletedDate varchar(40),
#UserID int = null
AS
BEGIN
IF #CompletedDate = ''
SET #CompletedDate = NULL
--for some forms such as Clinical Input - Initial, there should only have one form filled for a youth. If that is the case and the event has existed, just return that one.
DECLARE #EID int
SET #EID = dbo.fn_GetExistingOnlyOneEventID(#PatientID, #ProgramID, #FormID)
PRINT #eid
IF #EID <> -99
BEGIN
SELECT
#EID AS 'EventID'
RETURN
END
DECLARE #TxCycleID int
DECLARE #TxCyclePhaseTypeID int
DECLARE #TxCyclePhaseID int
DECLARE #seqNum int
DECLARE #NewEventID INT
--if there is no cycle for this patient for this program, then create one.
IF NOT EXISTS (SELECT * FROM TxCycle WHERE PatientID = #PatientID AND ProgID = #ProgramID)
BEGIN
INSERT INTO TxCycle
(OpenDate, PatientID, ProgID)
VALUES
(GETDate(), #PatientID, #ProgramID)
END
SELECT
#TxCycleID = Max(TxCycleID)
FROM TxCycle
WHERE
PatientID = #PatientID AND
ProgID = #ProgramID
--In this cycle, for the current phase type, get the max seq number
IF EXISTS (SELECT * FROM TxCyclePhase WHERE TxCycle = #TxCycleID)
BEGIN
SELECT
#seqNum = MAX(SeqNum)
FROM
TxCyclePhase
WHERE
TxCycle = #TxCycleID
SET #seqNum = #seqNum + 1
END
ELSE
BEGIN
SET #seqNum = 1
END
PRINT 'New Seq Num: ' + CONVERT(Varchar(5),#seqNum)
--greate a new seq number under the same phase
INSERT INTO TxCyclePhase
(Type, seqNum, TxCycle)
VALUES
(#PhaseTypeID, #seqNum, #TxCycleID)
--get the new ID, this will be used for the Event
SELECT
#TxCyclePhaseID = Max(TxCyclePhaseID)
FROM
TxCyclePhase
DECLARE #isFinal int
IF #Draft = 'Yes'
BEGIN
SET #isFinal = 0
END
ELSE
BEGIN
SET #isFinal = 1
END
IF EXISTS(SELECT * FROM LoginPassword WHERE ClinID = #ClinicianID AND AccessID IN (1,3))
BEGIN
IF NOT EXISTS (SELECT * FROM ClinPat WHERE ClinID = #ClinicianID AND PatientID = #PatientID)
BEGIN
INSERT INTO
ClinPat
(ClinID, PatientID)
VALUES
(#ClinicianID, #PatientID)
END
END
INSERT INTO FormPat
(PatientID, ClinID, FormID, TxCyclePhase, Date, Final, DataCollectionDate)
VALUES
(#PatientID, #ClinicianID, #FormID, #TxCyclePhaseID, GETDATE(), #isFinal, #CompletedDate)
SELECT #NewEventID = Scope_Identity()
SELECT #NewEventID AS 'EventID'
What am I doing wrong?
You need a RETURN at the bottom of your procedure.
RETURN #NewEventID
Here is a complete but simple example:
CREATE PROCEDURE [dbo].[uspExampleOne]
#Parameter1 INT
AS
BEGIN
SET NOCOUNT ON
RETURN 333
SET NOCOUNT OFF
END
GO
and
Declare #MyValue INT
EXEC #MyValue = [dbo].[uspExampleOne] 111
SELECT '#MyValueHere' = #MyValue
Result:
#MyValueHere
333
But a better design IMHO is to use an OUTPUT variable:
Why?
What happens when you need a second OUTPUT? What happens when the needed value is not an INT?
ALTER PROCEDURE [dbo].[uspExampleOne]
#Parameter1 INT ,
#OutParameter2 INT OUT
AS
BEGIN
SET NOCOUNT ON
Select #OutParameter2 = 444
RETURN 333
SET NOCOUNT OFF
END
GO
and
Declare #MyValue INT
Declare #OutParameterTwo INT
EXEC #MyValue = [dbo].[uspExampleOne] 111 , #OutParameterTwo OUT
SELECT '#MyValueHere' = #MyValue
Select '#OutParameterTwoHere' = #OutParameterTwo
Output
#MyValueHere
333
#OutParameterTwoHere
444
Below shows what I mean about "future proofing" with OUTPUT parameters
ALTER PROCEDURE [dbo].[uspExampleOne]
#Parameter1 INT ,
#OutParameter2 INT OUT,
#OutParameter3 VARCHAR(128) OUT
AS
BEGIN
SET NOCOUNT ON
Select #OutParameter2 = 444
Select #OutParameter3 = 'Better Design With Output Parameters. Not stuck with 1 return-value or data-type'
RETURN 0 /* everything went ok */
SET NOCOUNT OFF
END
GO
and the call to it
Declare #MyValue INT
Declare #OutParameterTwo INT
Declare #OutParameterThree VARCHAR(128)
EXEC #MyValue = [dbo].[uspExampleOne] 111 , #OutParameterTwo OUT , #OutParameterThree OUT
SELECT '#MyValueHere' = #MyValue
Select '#OutParameterTwoHere' = #OutParameterTwo , '#OutParameterThreeHere' = #OutParameterThree
and output
#OutParameterTwoHere #OutParameterThreeHere
444 Better Design With Output Parameters. Not stuck with 1 return-value or data-type

Sql Functions very slow

I am not expert in functions. Inherited the following function which is very slow
ALTER FUNCTION [dbo].[fn_Diagnosed]
( #clientId As int)
RETURNS NVARCHAR(10)
AS
BEGIN
DECLARE #result int;
Declare #return nvarchar(10);
set #result = (SELECT COUNT(*)
FROM dbo.AdditionalInfo
WHERE dbo.AdditionalInfo.Type = 'Diagnosed' and ClientId = #ClientId);
IF #result > 0
set #return = 'Yes'
ELSE
set #return = 'No';
return #return;
END
Is this the right way to write a function?
Your function looks fine. An index is not automatically created on a foreign key constraint. So, you should explicitly add an index, like this
CREATE INDEX ClientAdditionalInfo_ClientID
ON [dbo].[ClientAdditionalInfo]
(ClientID)
INCLUDE ([Type])

Use a stored procedure to get a list of nvarchar(20) values as a parameter

I'm developing a SQL Server 2012 stored procedure.
I need to return a list of nvarchar(20) values, and I don't know how can I do it. I have tried this:
Using a Table-Valued Parameters but it must be READONLY.
Doing a select to return that list. But stored procedures only
returns INT values.
Any idea?
UPDATE:
This is what I have done:
CREATE PROCEDURE [dbo].[GetAggregationChildren]
#parentCode nvarchar(20),
#codeLevel tinyint output,
#returnValue int output
AS
declare #childsLevelCount tinyint
, #invalidChildCodesCount int;
set nocount on;
-- ======== VALIDATION ==========
if NULLIF(#parentCode, '') IS NULL
begin
set #returnValue = -19;
return NULL; -- Parameter null or empty.
end
-- Check if parent exists in AGGREGATIONS table.
if not exists (Select top 1 CODE from AGGREGATIONS where CODE = #parentCode)
begin
set #returnValue = -11;
return NULL;
end
set #childsLevelCount = (select count(c.CODE_LEVEL) from CODES c where c.CODE in (Select CODE from AGGREGATION_CHILDS where PARENT_CODE = #parentCode) group by c.CODE_LEVEL);
-- If this aggregation has children codes with different values, it is an error.
if (#childsLevelCount > 1)
begin
set #returnValue = -201;
return NULL;
end
-- =========== CODE =============
set #returnValue = 0;
set #codeLevel = (select c.CODE_LEVEL from CODES c where c.CODE in (Select CODE from AGGREGATION_CHILDS where PARENT_CODE = #parentCode) group by c.CODE_LEVEL);
select CODE from AGGREGATION_CHILDS where PARENT_CODE = #parentCode;
But, I have no idea about how to return the result of this select:
select CODE from AGGREGATION_CHILDS where PARENT_CODE = #parentCode;
This stored procedure returns this on SQL Server Management Studio:
It is also returning a 0. I thought that the stored procedure is going to return the select result only.
I need the result in a parameter because I going to use in a SQLCommand like this one:
SqlParameter childsParam = new SqlParameter();
childsParam.ParameterName = "#childs";
childsParam.SqlDbType = System.Data.SqlDbType.Structured;
childsParam.Direction = System.Data.ParameterDirection.Input;
childsParam.Value = tvp;
parameters = new List<SqlParameter>();
parameters.Add(childsParam);
SqlConnection connection =
_context.Database.Connection as SqlConnection;
connection.Open();
SqlCommand cmd = new SqlCommand();
cmd.CommandText = storedProcedureName;
cmd.CommandType = CommandType.StoredProcedure;
if (parameters != null)
cmd.Parameters.AddRange(parameters.ToArray());
cmd.Connection = connection;
cmd.ExecuteNonQuery();
connection.Close();
Stored procedure returns only Integer?
No this is not 100% true. If you are using RETURN to return the values from your stored procedure then your statement is true else it is false.
If you want to return string from your stored procedure then you can use SELECT like this:
CREATE PROCEDURE myProc
AS
BEGIN
SELECT 'test'
END
And to return multiple values you can use it like
CREATE PROCEDURE myProc
#Value1 varchar(20) OUTPUT,
#Value2 varchar(20) OUTPUT
AS
SELECT #Value1 = 'test1', #Value2 = 'test2'
and call it like
DECLARE #Value1 varchar(20), #Value2 varchar(20)
exec myProc #Value1 OUTPUT, #Value2 OUTPUT
SELECT #Value1, #Value1
Stored procedures return the type of the field in the SELECT statement. You can use CAST and CONVERT to change the types. For example:
SELECT CAST(field AS NVARCHAR(20))
With table value parameters you can set the field type on creation:
CREATE TYPE JobSpecifications AS TABLE
(JobName VARCHAR(50), AvailableDate Date );
you can use a temporary table to recuperate your list from the stored procedure, like the example below :
create proc Test
AS BEGIN
SELECT CAST('jkj' AS NVARCHAR(20)) value
END
DECLARE #tmp TABLE(value nvarchar(20))
INSERT INTO #tmp EXEC GhaziTest
SELECT * from #tmp

TSQL: Incorrect syntax near function name

I have this one: (updated)
IF OBJECT_ID (N'dbo.fn_getProductCatPurchased', N'IF') IS NOT NULL
DROP FUNCTION [dbo].fn_getProductCatPurchased;
GO
then the function starts:
CREATE FUNCTION [dbo].fn_getProductCatPurchased
(
#Custno varchar(100),
#PriceType varchar(100)
)
RETURNS int
AS
BEGIN
DECLARE #groups int;
SELECT #groups = COUNT(DISTINCT(prodcat))
FROM SALES
WHERE custno = #Custno
AND pricetype=#PriceType
IF (#group IS NULL)
SET #group = 0;
RETURN #group;
END;
GO
When I try to save the function an error was thrown:
Incorrect syntax near fn_getProductCatPurchased
What am I doing wrong?
Not sure if this is the cause of your error, but it's definitely a problem.
You are declaring a variable called #groups:
DECLARE #groups int;
but proceed to use #group without an s instead:
IF (#group IS NULL)
SET #group = 0;
RETURN #group;

Resources