How to find missing pairs in many-to-many table? - sql-server

Given: Users and Groups, as well as many-to-many GroupUsers table.
It is necessary that each pair of users has its own paired group, provided that there can be groups with more or less users.
How to check and create the missing paired groups?
Link to SQL Fiddle
create table dbo.Users (
Id int not null,
Name nvarchar(50) not null,
constraint PK_Users primary key clustered (Id)
);
create table dbo.Groups (
Id int not null,
Name nvarchar(50) not null,
constraint PK_Groups primary key clustered (Id)
);
create table dbo.GroupUsers (
GroupId int not null ,
UserId int not null ,
constraint PK_GroupUsers primary key clustered (GroupId, UserId),
constraint FK_GroupUsers_GroupId foreign key (GroupId) references dbo.Groups (Id),
constraint FK_GroupUsers_UserId foreign key (UserId) references dbo.Users (Id)
);
insert into dbo.Users values (1, 'Anna'), (2, 'Berta'), (3, 'Carlie'), (4, 'Dana'), (5, 'Emil');
insert into dbo.Groups values (1, 'Anna-Berta'), (2, 'Anna-Carlie'), (3, 'Anna-Berta-Carlie');
insert into dbo.GroupUsers values
(1,1), (1, 2), -- 'Anna-Berta' group
(2,1), (2, 3), -- 'Anna-Carlie' group
(3,1), (3, 2), (3, 3); -- 'Anna-Berta-Carlie' group
How to find and create missing "Pair Group" for user Anna?
So Anna has a Pair Group with all other users
and all the Anna's Pair Groups must have exactly two users,
although any user including Anna may have a group which includes more or less than two users.
UPDATE 2019-12-15:
So here (Link to SQL Fiddle) is my favorite solution (so far) of how to find missing paired groups and their users (Thanks to answer from #Kari F.)
declare #UserId int = 1;
with cte as
(
select
VirtualGroupId = row_number() over(order by p.Id desc) * -1,
GroupName = concat( u.Name, '_', p.Name),
CurrentUserId = u.Id,
OtherUserId = p.Id
from
dbo.Users u inner join dbo.Users p on u.Id = #UserId and p.Id <> #UserId
where
concat('_', u.Id, '_', p.Id) not in
(
select
(
select concat('_', gu.UserId)
from dbo.GroupUsers gu
where gu.GroupId = g.Id
order by case when gu.UserId = #UserId then 0 else 1 end
for xml path ('')
)
from
dbo.Groups g
)
)
select VirtualGroupId, GroupName, UserId = CurrentUserId from cte
union all
select VirtualGroupId, GroupName, UserId = OtherUserId from cte
order by VirtualGroupId, UserId

What about this simple solution:
select u.Id, p.Id
from dbo.Users u
cross join dbo.Users p
where u.Id = 1 and p.Id <> 1
and u.Name + '-' + p.Name not in (select g.Name from dbo.Groups g);
http://sqlfiddle.com/#!18/3c30f/16
Second solution taking into account you comment
with required_groups as (
select u.Id as userId, p.Id pairId
from dbo.Users u
cross join dbo.Users p
where u.Id = 1 and p.Id <> 1
),
existing_groups as (
select annas_groups.UserId as userId, pairs_groups.UserId as pairId
from (
select *
from GroupUsers gu
where gu.UserId = 1
and gu.GroupId in (
select x.GroupId from GroupUsers x
group by x.GroupId
having count(*) = 2
)
) annas_groups
inner join (
select *
from GroupUsers gu
where
gu.UserId <> 1
and gu.GroupId in (
select x.GroupId from GroupUsers x
group by x.GroupId
having count(*) = 2
)
) pairs_groups on annas_groups.GroupId = pairs_groups.GroupId
)
select *
from required_groups rg
where not exists (
select 1
from existing_groups eg
where eg.UserId = rg.UserId
and eg.PairId = rg.PairId
)
http://sqlfiddle.com/#!18/3c30f/34
A bit more complex but still readable, I think.

I have tried to solve the problem using a loop approach, this is because the id from the groups table must be supplied and is not an identity column otherwise the implementation becomes simpler.
I've also used a keyuser variable to handle the fact that it is "Anna" who must be paired and not some other user. This avoids simply hard coding the userid value.
create table #Users (
Id int not null,
Name nvarchar(50) not null,
constraint PK_Users primary key clustered (Id)
);
create table #Groups (
Id int not null,
Name nvarchar(50) not null,
constraint PK_Groups primary key clustered (Id)
);
create table #GroupUsers (
GroupId int not null ,
UserId int not null ,
constraint PK_GroupUsers primary key clustered (GroupId, UserId),
constraint FK_GroupUsers_GroupId foreign key (GroupId) references #Groups (Id),
constraint FK_GroupUsers_UserId foreign key (UserId) references #Users (Id)
);
insert into #Users values (1, 'Anna'), (2, 'Berta'), (3, 'Carlie'), (4, 'Dana'), (5, 'Emil');
insert into #Groups values (1, 'Anna-Berta'), (2, 'Anna-Carlie'), (3, 'Anna-Berta-Carlie');
insert into #GroupUsers values
(1,1), (1, 2), -- 'Anna-Berta' group
(2,1), (2, 3), -- 'Anna-Carlie' group
(3,1), (3, 2), (3, 3); -- 'Anna-Berta-Carlie' group
declare #sql nvarchar(max) = '';
declare #nextgroupid int = 0;
declare #keyuser int = 0;
select #keyuser = ID from #Users where [Name]='Anna';
while exists (select 1 from #Users u inner join #Users u2 on u.Id=1 where u2.Id not in (select userid from #GroupUsers))
begin
select #nextgroupid = MAX(id)+1 from #Groups;
set #sql = 'insert #groups (id, [name]) select top 1 ' + CAST(#nextgroupid as nvarchar(3)) + ', u.[name] + ''-'' + u2.[name] as missingusergroup from #Users u inner join #Users u2 on u.Id= ' + CAST(#keyuser as nvarchar(3)) + ' where u2.Id not in (select userid from #GroupUsers) order by u2.id;';
exec(#sql);
set #sql = 'insert #groupusers (groupid, userid) select top 1 ' + CAST(#nextgroupid as nvarchar(3)) + ', u2.id from #Users u inner join #Users u2 on u.Id= ' + CAST(#keyuser as nvarchar(3)) + ' where u2.Id not in (select userid from #GroupUsers) union select top 1 '+ CAST(#nextgroupid as nvarchar(3)) +', u.id from #Users u where u.id= ' + CAST(#keyuser as nvarchar(3)) + ' order by u2.id;';
exec(#sql);
end
select * from #GroupUsers;
select * from #Groups;
drop table #Groups, #GroupUsers, #Users;

Related

Sql server subquery to be rewritten using Joins

I have this pasted subquery and i need to rewrite it using only joins ( no subquery).
Tried multiple times for close to a month but in vain.
Request you to help me out.
SELECT * FROM wp_user WHERE userId NOT IN
(SELECT u.userId FROM wp_user as u, wp_luncher as i, wp_subscription as s
WHERE u.userId = i.luncherId
and i.luncherId = s.luncherid)
and CreationDate between '20181001' and '20181015';
Tables involved :-
CREATE TABLE wp_user (
userId int identity(1,1) PRIMARY KEY NOT NULL,
userName varchar(20) NOT NULL,
CreationDate date NOT NULL
);
CREATE TABLE wp_luncher (
luncherId int PRIMARY KEY NOT NULL,
parentId int FOREIGN KEY REFERENCES wp_user(userId)
);
CREATE TABLE wp_subscription (
SubId int PRIMARY KEY NOT NULL,
luncherId int FOREIGN KEY REFERENCES wp_luncher(luncherId)
);
Try this query.
You can use OUTER APPLY.
SELECT *
FROM wp_user wp
OUTER APPLY
(
SELECT i.luncherId
FROM wp_luncher as i, wp_subscription as s
WHERE i.luncherId = wp.userId
and i.luncherId = s.luncherid
) I
WHERE I.luncherId IS NOT NULL
and CreationDate between '20181001' and '20181015';
Another option inserts the subquery's data into a table variable.
DECLARE #TempTable AS TABLE (UserId INT)
INSERT INTO #TempTable
SELECT i.luncherId
FROM wp_luncher as i, wp_subscription as s
WHERE i.luncherId = s.luncherid
SELECT *
FROM wp_user wp
LEFT JOIN #TempTable I ON I.UserId = wp.userId
WHERE I.UserId IS NOT NULL
and CreationDate between '20181001' and '20181015';

How to get id's of parent ids for inserting children

I have Parent and Child table.
The goal is to duplicate the records, except with new primary keys.
Original Tables
Parent(id)
1
Child(id,parentId, data)
1,1
2,1
After insert:
Parent
1
2
Child
1,1
2,1
3,2
4,2
How do I do that? The part I am having trouble with is getting the new parent key for use with the child records.
This is what I have come up with so far.
--DECLARE VARS
declare #currentMetadataDocumentSetId int = 1, --Ohio
#newMetadataDocumentSetid int = 3; --PA
--CLEANUP
IF OBJECT_ID('tempdb..#tempFileRowMap') IS NOT NULL
/*Then it exists*/
DROP TABLE #tempFileRowMap
--Remove existing file row maps.
delete from file_row_map where metadata_document_set_id = #newMetadataDocumentSetid;
--Create a temptable to hold data to be copied.
Select [edi_document_code],
[functional_group],
[description],
3 as [metadata_document_set_id],
[document_name],
[incoming_file_row_subtype],
[metadata_document_id],
[document_subcode],
[outgoing_file_row_subtype],
[asi_type_code],
[asi_action_code],
[metadata_document_set],
file_row_map_id as orig_file_row_map_id
into #tempFileRowMap
from file_row_map fileRowMap
where metadata_document_set_id = #currentMetadataDocumentSetId;
--Select * from #tempFileRowMap;
Insert into file_row_map select
[edi_document_code],
[functional_group],
[description],
[metadata_document_set_id],
[document_name],
[incoming_file_row_subtype],
[metadata_document_id],
[document_subcode],
[outgoing_file_row_subtype],
[asi_type_code],
[asi_action_code],
[metadata_document_set]
from #tempFileRowMap
--Show Results
Select * from file_row_map fileRowMap where fileRowMap.metadata_document_set_id = #newMetadataDocumentSetid
--Update Detail
Select
[file_row_map_id],
[file_row_column],
[element_code],
[element_metadata_id],
[col_description],
[example],
[translate],
[is_used],
[is_mapped],
[page_num],
[subcode],
[qualifier],
[loop_code],
[loop_subcode],
[default_value],
[delete_flag]
into #tempFileRowMapDetail
from [dbo].[file_row_map_detail] d
left join #tempFileRowMap m
on m.orig_file_row_map_id = d.file_row_map_id
select * from #tempFileRowMapDetail
Simply use OUTPUT clause for getting exact Parent Table Primary Key values.
Lets build Example Schema for your case
--For Capturing inserted ID
CREATE TABLE #ID_CAPTURE (PARENT_ID INT,ORDER_NME VARCHAR(20));
--Your Intermidiate Data To insert into Actual Tables
CREATE TABLE #DUMMY_TABLE (ORDER_NME VARCHAR(20), ITEM_NME VARCHAR(20));
--Actual Tables
CREATE TABLE #ORDER_PARENT (ORDER_ID INT IDENTITY,ORDER_NME VARCHAR(20))
CREATE TABLE #ORDER_CHILD (CHILD_ID INT IDENTITY ,ORDER_ID INT, ORDER_NME VARCHAR(20))
INSERT INTO #DUMMY_TABLE
SELECT 'BILL1','Oil'
UNION ALL
SELECT 'BILL1', 'Gas'
UNION ALL
SELECT 'BILL2', 'Diesel'
Now do Inserts in Parent & Child Tables
INSERT INTO #ORDER_PARENT
OUTPUT inserted.ORDER_ID, inserted.ORDER_NME into #ID_CAPTURE
SELECT DISTINCT ORDER_NME FROM #DUMMY_TABLE
INSERT INTO #ORDER_CHILD
SELECT C.PARENT_ID, ITEM_NME FROM #DUMMY_TABLE D
INNER JOIN #ID_CAPTURE C ON D.ORDER_NME = C.ORDER_NME
SELECT * FROM #ID_CAPTURE
SELECT * FROM #ORDER_CHILD
There are other ways to get Inserted Identity values.
See documentation ##IDENTITY (Transact-SQL) , SCOPE_IDENTITY
Try following approach:
DECLARE #Table1 TABLE (
ID INT NOT NULL PRIMARY KEY,
ParentID INT NULL, -- FK
[Desc] VARCHAR(50) NOT NULL
);
INSERT #Table1 (ID, ParentID, [Desc])
VALUES
(1, NULL, 'A'),
(2, 1, 'AA.1'),
(3, 1, 'AA.2'),
(4, NULL, 'B'),
(5, 4, 'BB.1'),
(6, 4, 'BB.2'),
(7, 4, 'BB.3'),
(8, 7, 'BBB.1');
DECLARE #ParentID INT = 4;
DECLARE #LastID INT = (SELECT TOP(1) ID FROM #Table1 x ORDER BY x.ID DESC)
IF #LastID IS NULL
BEGIN
RAISERROR('Invalid call', 16, 1)
--RETURN ?
END
SELECT #LastID AS LastID;
/*
LastID
-----------
8
*/
DECLARE #RemapIDs TABLE (
OldID INT NOT NULL PRIMARY KEY,
[NewID] INT NOT NULL UNIQUE
);
WITH CteRecursion
AS (
SELECT 1 AS Lvl, crt.ID, crt.ParentID --, crt.[Desc]
FROM #Table1 crt
WHERE crt.ID = #ParentID
UNION ALL
SELECT cld.Lvl + 1 AS Lvl, crt.ID, crt.ParentID --, crt.[Desc]
FROM #Table1 crt
JOIN CteRecursion cld ON crt.ParentID = cld.ID
)
INSERT #RemapIDs (OldID, [NewID])
SELECT r.ID, #LastID + ROW_NUMBER() OVER(ORDER BY r.Lvl) AS [NewID]
FROM CteRecursion r;
--INSERT #Table1 (ID, ParentID, [Desc])
SELECT nc.[NewID] AS ID, np.[NewID] AS ParentID, o.[Desc]
FROM #Table1 o -- old
JOIN #RemapIDs nc /*new child ID*/ ON o.ID = nc.OldID
LEFT JOIN #RemapIDs np /*new parent ID*/ ON o.ParentID = np.OldID
/*
ID ParentID Desc
----------- ----------- --------------------------------------------------
9 NULL B
10 9 BB.1
11 9 BB.2
12 9 BB.3
13 12 BBB.1
*/
Note: with some minor changes should work w. many ParentIDs values.

Avoid referring table two times in the WHERE clause

Following is a simplified version of my database in SQL Server 2005. I need to select employees based on business units. Each employee has home department, parent department and visiting department. Based on the department, business unit can be found out.
For an employee, if the HomeDeptID = ParentDeptID, then
#SearchBusinessUnitCD should be present for the VisitingDeptID.
If HomeDeptID <> ParentDeptID, then #SearchBusinessUnitCD should be
present for the ParentDeptID.
Following query works fine. But it has scan on the #DepartmentBusinesses table two times. Is there a way to use the table #DepartmentBusinesses only once by making it as a CASE statement or similar?
DECLARE #SearchBusinessUnitCD CHAR(3)
SET #SearchBusinessUnitCD = 'B'
--IF HomeDeptID = ParentDeptID, then #SearchBusinessUnitCD should be present for the VisitingDeptID
--IF HomeDeptID <> ParentDeptID, then #SearchBusinessUnitCD should be present for the ParentDeptID
CREATE TABLE #DepartmentBusinesses (DeptID INT, BusinessUnitCD CHAR(3))
INSERT INTO #DepartmentBusinesses
SELECT 1, 'A' UNION ALL
SELECT 2, 'B'
CREATE NONCLUSTERED INDEX IX_DepartmentBusinesses_DeptIDBusinessUnitCD ON #DepartmentBusinesses (DeptID,BusinessUnitCD)
DECLARE #Employees TABLE (EmpID INT, HomeDeptID INT, ParentDeptID INT, VisitingDeptID INT)
INSERT INTO #Employees
SELECT 1, 1, 1, 2 UNION ALL
SELECT 2, 2, 1, 3
SELECT *
FROM #Employees
WHERE
(
HomeDeptID = ParentDeptID
AND
EXISTS (
SELECT 1
FROM #DepartmentBusinesses
WHERE DeptID = VisitingDeptID
AND BusinessUnitCD = #SearchBusinessUnitCD)
)
OR
(
HomeDeptID <> ParentDeptID
AND
EXISTS (
SELECT 1
FROM #DepartmentBusinesses
WHERE DeptID = ParentDeptID
AND BusinessUnitCD = #SearchBusinessUnitCD
)
)
DROP TABLE #DepartmentBusinesses
Plan
SELECT *
FROM #Employees e
WHERE EXISTS (
SELECT 1
FROM #DepartmentBusinesses t
WHERE t.BusinessUnitCD = #SearchBusinessUnitCD
AND (
(e.HomeDeptID = e.ParentDeptID AND t.DeptID = e.VisitingDeptID)
OR
(e.HomeDeptID != e.ParentDeptID AND t.DeptID = e.ParentDeptID)
)
)
You can give this a try:
SELECT e.*
FROM #Employees AS e
INNER JOIN #DepartmentBusinesses AS d
ON (d.DeptID = e.VisitingDeptID AND e.HomeDeptID = e.ParentDeptID) OR
(d.DeptID = e.ParentDeptID AND e.HomeDeptID <> e.ParentDeptID)
WHERE d.BusinessUnitCD = #SearchBusinessUnitCD

Deleting 'equivalent' data from two tables

I need to perform the following pseudo logic in a SQL Server 2012 procedure, based around a table variable and a table declared as such:
DECLARE #tmp TABLE
(
ID int IDENTITY(1,1),
UserID int NOT NULL,
SgsID int NOT NULL
)
CREATE TABLE #Table1
(
ID int IDENTITY(1,1),
UserID int NOT NULL,
SgsID int NOT NULL
)
For each row of data in table variable #tmp
Delete rows from Table1 where UserID/SgsID combinations match UserID/SgsID in Table1
Delete those UserID/SgsID combinations from #tmp that have been deleted from Table1
I've been researching different approaches, such as using OUTPUT INTO and INTERSECT, but cannot write a query that deletes across two tables (in fact I don't think it is even possible).
I have achieved the above steps by using the following code, however, I was wondering if any T-SQL pro's may be able to suggest a more succinct/efficient approach?
See SQLFiddle for online version
CREATE TABLE #Table1
(
ID int IDENTITY(1,1),
UserID int NOT NULL,
SgsID int NOT NULL
)
INSERT INTO #Table1 (UserID, SgsID) VALUES (5, 99)
INSERT INTO #Table1 (UserID, SgsID) VALUES (10, 89)
INSERT INTO #Table1 (UserID, SgsID) VALUES (150, 79)
INSERT INTO #Table1 (UserID, SgsID) VALUES (200, 69)
INSERT INTO #Table1 (UserID, SgsID) VALUES (250, 59)
SELECT * FROM #Table1
DECLARE #tmp TABLE
(
ID int IDENTITY(1,1),
UserID int NOT NULL,
SgsID int NOT NULL
)
INSERT INTO #tmp (UserID, SgsID) VALUES (150, 79)
INSERT INTO #tmp (UserID, SgsID) VALUES (200, 69)
INSERT INTO #tmp (UserID, SgsID) VALUES (250, 59)
INSERT INTO #tmp (UserID, SgsID) VALUES (999, 49)
SELECT * FROM #tmp
DECLARE #tbl_commonRows TABLE (UserID int, SgsID int)
INSERT INTO #tbl_commonRows
(
UserID,
SgsID
)
SELECT
UserID,
SgsID
FROM
#Table1
INTERSECT
SELECT
UserID,
SgsID
FROM
#tmp
DELETE FROM
#Table1
WHERE
(ID IN (
SELECT
ID
FROM
#Table1 t1 INNER JOIN
#tbl_commonRows c ON c.UserID = t1.UserID AND c.SgsID = t1.SgsID))
DELETE FROM
#tmp
WHERE
(ID IN (
SELECT
ID
FROM
#tmp t2 INNER JOIN
#tbl_commonrows c ON c.UserID = t2.UserID AND c.SgsID = t2.SgsID))
SELECT * FROM #Table1
SELECT * FROM #tmp
DROP TABLE #Table1
Here's solution:
DECLARE #tmp_ids TABLE (
id1 INT,
id2 INT
)
INSERT INTO #tmp_ids (id1, id2)
SELECT
t1.id,
t2.id
FROM Table1 t1
INNER JOIN tmp t2
on (t1.UserID = t2.UserID AND t1.SgsID = t2.SgsID)
DELETE FROM Table1
WHERE id IN (SELECT id1 FROM #tmp_ids)
DELETE FROM tmp
WHERE id IN (SELECT id2 FROM #tmp_ids)
Keep in mind - i created physical tables tmp and Table1
You can take advantage of the fact that the OUTPUT command can take more than INSERTED and DELETED columns for deletes (but not inserts, sadly):
DECLARE #output TABLE (id int)
DELETE FROM tbl
OUTPUT tmp.ID INTO #output(id)
FROM #Table1 tbl
JOIN #tmp tmp
ON tbl.UserID = tmp.UserID
AND tbl.SgsID = tmp.SgsID
DELETE FROM tmp
FROM #tmp tmp
JOIN #Output outp ON tmp.id = outp.id
Have you looked into using MERGE for this? Might be another option, and the syntax is nice and easy to follow.
MERGE (Transact-SQL)

Sql select to string

I have a query that returns a result set of one column, but I want it to be converted into one long string for use as a subquery. I.E. I want to be able to do:
SELECT user.*, (SELECT group_name FROM user_groups WHERE userID = user.ID) AS Groups FROM user
However, that will fail because a user can belong to more than one group. For example if the user belong to {"writer", "editor"} I want it to return a string like this: "writer, editor".
How can I do this?
You can use FOR XML to do this pivoting action. Here is a working sample.
set nocount on
declare #Users Table
(
UserId int,
UserName varchar (20)
)
declare #UserGroups Table
(
GroupId int,
UserId int,
GroupName varchar (20)
)
Insert #Users Values (1, 'Tim')
Insert #Users Values (2, 'Jane')
Insert #Users Values (3, 'Hal')
Insert #UserGroups Values (1, 1, 'Admin')
Insert #UserGroups Values (2, 1, 'Power-users')
Insert #UserGroups Values (3, 2, 'Noobs')
Insert #UserGroups Values (4, 2, 'Users')
Insert #UserGroups Values (5, 3, 'Noobs')
/* How this works */
SELECT 'FirstStep : Users table'
SELECT * FROM #Users
SELECT 'NextStep : User Groups table'
SELECT * FROM #UserGroups
SELECT 'NextStep : Users & UserGroups table'
SELECT *
FROM #Users U
INNER JOIN #UserGroups UG ON U.UserId = UG.UserId
SELECT 'NextStep : Just get the groups for one user (UserId = 2)'
SELECT GroupName
FROM #UserGroups UG
WHERE UG.userID = 2
ORDER BY GroupName
SELECT 'NextStep : When you use XML Path the output comes out in XML format'
SELECT GroupName
FROM #UserGroups UG
WHERE UG.userID = 2
ORDER BY GroupName
FOR XML PATH('') -- XML Path added
SELECT 'NextStep : When you remove the column name the XML tags go away,
but it looks ugly because there is no separator'
SELECT GroupName + '' -- Added an empty string to remove the column name
FROM #UserGroups UG
WHERE UG.userID = 2
ORDER BY GroupName
FOR XML PATH('')
SELECT 'NextStep : Add a separator
We add it to the beginning instead of the end so that we can STUFF later on.'
SELECT ', ' + GroupName -- Added an empty string to remove the column name
FROM #UserGroups UG
WHERE UG.userID = 2
ORDER BY GroupName
FOR XML PATH('')
SELECT 'NextStep : I don''t like that ugly XML column name. Let me give it my own name'
SELECT
(
SELECT ', ' + GroupName
FROM #UserGroups UG
WHERE UG.userID = 2
ORDER BY GroupName
FOR XML PATH('')
) as UserGroups
SELECT 'NextStep : STUFF the extra comma'
SELECT STUFF
(
(
SELECT ', ' + GroupName
FROM #UserGroups UG
WHERE UG.userID = 2
ORDER BY GroupName
FOR XML PATH('')
),
1, 2, ''
) as UserGroups
SELECT 'NextStep : Now join it with the Users table by the UserId and get the UserName'
SELECT
U.UserName,
STUFF
(
(
SELECT ', ' + GroupName
FROM #UserGroups UG
WHERE UG.userID = U.userID
ORDER BY GroupName
FOR XML PATH('')
),
1, 2, ''
) as UserGroups
FROM #Users U

Resources