SQL Query -IF EXISTS and WITH - sql

Hi have a stored procedure with WITH keyword that returns a resultset as follows
WITH p as(...),
mp as (select *, x.name from p left join x on ...)
Now i want to say
IF EXISTS (select * from mp where pid is not null and id != pid)
THEN
BEGIN
SELECT * FROM cp left join mp on ...
END
ELSE
BEGIN SELECT * FROM mp where...
END
Here i am facing some syntax error this select should be returned back from stored procedure
UPDATE
I have put it as follows
SELECT DISTINCT *
FROM (
--get all parent records that matches RIDE ID
SELECT * FROM mp WHERE pid is null or pid = id
UNION --get parent of a child that matches RIDE ID
SELECT pl.* [i have specified exact same columns in this select]
FROM pl
left join mp on pl.plc_id = mp.PrimaryPlacementId
left join reg on reg.usr_id = mp.plc_usr and isnull(reg.usr_isdeleted, 0) = 0 --and reg.usr_active = 1
left join ut on ut.cut_id = mp.plc_unit
left join f on f.fac_id = mp.plc_facility and isnull(f.fac_isdeleted, 0) = 0
left join s on s.sem_id = mp.plc_semester and isnull(s.sem_isdeleted, 0) = 0
WHERE mp.pid != id AND mp.pid is not null
) x
This one seems to work ok - basically pid contains the parent id
My mp=pl table has the following structure
id | name | pid
1 | sam | 1
2 | sid | 1
so basically first is the parent record and second is the child record.
so what i want is - if i search 'sam' then record 1 is returned
and if i search 'sid' again record one is returned
i.e if u search by parent id or child id the parent record should be returned or those whose pid is null

Related

How can I check that ALL tags apply to these items in SQL?

I have four tables: items relationships tags item_to_tags. Relationships can connect two items and items_to_tags connect tags to items like this:
items
id
...
0
...
1
...
2
...
3
...
4
...
relationships
source_item_id
target_item_id
0
1
0
2
1
3
1
4
tags
id
name
0
A
1
B
items_to_tags
item_id
tag_id
1
0
1
1
2
1
3
0
3
1
4
1
The above would give a graph that looks like this
I've created a recursive function that will give me all of the descendants starting from a specific item:
CREATE OR REPLACE FUNCTION get_items_descendants(item_id int)
RETURNS SETOF items AS $$
WITH RECURSIVE descendants AS (
SELECT i.id, r.target_item_id
FROM items i
LEFT OUTER JOIN relationships r ON (i.id = r.source_item_id)
WHERE i.id = item_id
UNION
SELECT i.id, r.target_item_id
FROM descendants a
JOIN items i ON (a.target_item_id = i.id)
LEFT OUTER JOIN relationships r ON (i.id = r.source_item_id)
)
SELECT * FROM items i WHERE i.id IN (SELECT id FROM descendants WHERE id != item_id);
$$ LANGUAGE sql STABLE;
DBFiddle here: https://www.db-fiddle.com/f/teicDervXhN3AmEPfYzNn2/1
For example, if you run SELECT * FROM get_items_descendants(1); then it will return items 3 and 4 since they're the descendants of item 1.
I then updated it to allow a tag filter to be applied like this:
CREATE OR REPLACE FUNCTION get_items_descendants(item_id int, tag_filters integer[] = array[]::integer[])
RETURNS SETOF items AS $$
WITH RECURSIVE descendants AS (
SELECT i.id, r.target_item_id
FROM items i
LEFT OUTER JOIN relationships r ON (i.id = r.source_item_id)
WHERE i.id = item_id
UNION
SELECT i.id, r.target_item_id
FROM descendants a
JOIN items i ON (a.target_item_id = i.id)
LEFT OUTER JOIN relationships r ON (i.id = r.source_item_id)
LEFT OUTER JOIN items_to_tags t ON (i.id = t.item_id)
WHERE cardinality(tag_filters::integer[]) = 0 OR t.tag_id = ANY(tag_filters)
)
SELECT * FROM items i WHERE i.id IN (SELECT id FROM descendants WHERE id != item_id);
$$ LANGUAGE sql STABLE;
DBFiddle here: https://www.db-fiddle.com/f/xvKwN96kJnBqZ59QUXbYvj/1
Now calling SELECT * FROM get_items_descendants(1, ARRAY[0]); only returns item 3 because item 4 doesn't have the tag A. Passing ARRAY[0,1] or ARRAY[1] returns both 3 and 4 because they both have tag B and the t.tag_id = ANY(tag_filters) only requires one of the tag filters to exist.
What i'm struggling with is updating the function so that ALL tags must exist if they're defined in the tag_filters parameter. So ARRAY[0,1] will only return item 3.
Is this possible? The data structure above is fairly locked so can't be changed too much as it's already in production. Also, if anyone has any advice on the functions above that would be much appreciated as i'm quite new to SQL.
I ended up having to do a solution for when none of the tags apply as well as any of and all of. This is my current solution if anyone finds it useful. If anyone has any ways of improving it then please let me know:
CREATE OR REPLACE FUNCTION get_items_descendants(item_id int, tag_filters integer[] = array[]::integer[], operation text = 'any')
RETURNS SETOF items AS $$
WITH RECURSIVE descendants AS (
SELECT i.id, r.target_item_id, -1 as tag_id
FROM items i
LEFT OUTER JOIN relationships r ON (i.id = r.source_item_id)
WHERE i.id = item_id
UNION
SELECT i.id, r.target_item_id, t.tag_id
FROM descendants d
JOIN items i ON (d.target_item_id = i.id)
LEFT OUTER JOIN relationships r ON (i.id = r.source_item_id)
LEFT OUTER JOIN items_to_tags t ON (i.id = t.item_id)
WHERE (operation = 'none' AND NOT EXISTS (SELECT * FROM items_to_tags WHERE item_id = i.id AND tag_id = ANY(tag_filters)))
OR (operation != 'none' AND (cardinality(tag_filters::integer[]) = 0 OR t.tag_id = ANY(tag_filters)))
)
SELECT * FROM items i
WHERE (operation = 'all'
AND i.id IN (
SELECT id FROM (
SELECT id, array_agg(tag_id) as tag_ids
FROM descendants d
WHERE id != item_id
GROUP BY id
) as d
WHERE d.tag_ids #> tag_filters
)
)
OR (operation != 'all'
AND i.id IN (
SELECT id FROM descendants WHERE id != item_id
)
);
$$ LANGUAGE sql STABLE;

LEFT OUTER JOIN not always matching

I'm starting with a SQL query with a couple of joins and I'm getting the exact data I expect. This is what the current query is.
SELECT DISTINCT o.OrganizationHierarchyUnitLevelFourCd, o.OrganizationHierarchyUnitLevelThreeNm, o.OrganizationHierarchyUnitLevelFourNm
FROM Lab_Space l
JOIN Worker w ON l.Contact_WWID = w.WWID AND w.Employee_Status_Code = 'A'
JOIN Org_Hierarchy o ON o.OrganizationHierarchyUnitLevelThreeNm IS NOT NULL AND w.Org_Hierarchy_Unit_Cd = o.OrganizationHierarchyUnitCd
ORDER BY o.OrganizationHierarchyUnitLevelThreeNm, o.OrganizationHierarchyUnitLevelFourNm
This ends up with a row like
1234 | Finance | IT
Now I've created a new table, where I'm tracking whether or not I want to include the organization in my output. That table just has two columns, an org ID and a bit field. So I thought I could LEFT OUTER JOIN, since the second table won't have data on all orgs, so I expanded the query to this:
SELECT DISTINCT o.OrganizationHierarchyUnitLevelFourCd, o.OrganizationHierarchyUnitLevelThreeNm, o.OrganizationHierarchyUnitLevelFourNm, v.Include
FROM Lab_Space l
JOIN Worker w ON l.Contact_WWID = w.WWID AND w.Employee_Status_Code = 'A'
JOIN Org_Hierarchy o ON o.OrganizationHierarchyUnitLevelThreeNm IS NOT NULL AND w.Org_Hierarchy_Unit_Cd = o.OrganizationHierarchyUnitCd
LEFT OUTER JOIN Validation_Email_Org_Unit_Inclusion v ON o.OrganizationHierarchyUnitCd = v.OrganizationHierarchyUnitCd
ORDER BY o.OrganizationHierarchyUnitLevelThreeNm, o.OrganizationHierarchyUnitLevelFourNm
The problem I have is now I end up with rows like so:
1234 | Finance | IT | NULL
1234 | Finance | IT | 1
Since the Validation_Email_Org_Unit_Inclusion table includes a 1 for the 1234 org, I would expect to just get a single row with a value of 1, not include the row with NULL.
What have I done wrong?
You output OrganizationHierarchyUnitLevelFourCd but currently join on OrganizationHierarchyUnitCd. Join on the same column you output to get the corresponding value.
SELECT DISTINCT o.OrganizationHierarchyUnitLevelFourCd, ...
...
LEFT OUTER JOIN Validation_Email_Org_Unit_Inclusion v ON o.OrganizationHierarchyUnitLevelFourCd = v.OrganizationHierarchyUnitCd
...

Look for data where child doesn't exisit

How to look in Table C for those inspectors who have got ParentID but not child.
Table A has both parent and child data. Parent ID 0 is for parents and child has their parent ID.
In Table C, one inspector can have many parents and many childs.
I need to run a query to look for those inspectors who have got parents but not child.
Table A Table B Table C
-------- ------- -------
DisciplineID(PK) InspectorID(PK) ID (PK)
ParentID DisciplineID(FK)
InspectorID (Fk)
Table A Table C
In above mentioned data, Inspector 7239 and 7240 only have parent but not child. So query should return those two not 7242 because he has both parent and childs.
Use EXISTS and NOT EXISTS:
SELECT c.ID, c.InspectorID, c.DisciplineID
FROM dbo.TableC c
WHERE EXISTS
(
SELECT 1 FROM dbo.TableA a
WHERE a.DisciplineID = c.DisciplineID
AND a.ParentID = 0 -- parent exists
)
AND NOT EXISTS
(
SELECT 1 FROM dbo.TableC c2
WHERE c.InspectorID = c2.InspectorID
AND c.ID <> c2.ID -- look for another record with this InspectorID
AND EXISTS
(
SELECT 1 FROM dbo.TableA a
WHERE a.DisciplineID = c2.DisciplineID
AND a.ParentID <> 0 -- no child exists
)
)
I would start with a pre-qualifying query per discipline based on those having a count of entries that HAVE a parent ID = 0 but also no records as child... Join that result to your TableC
SELECT
c.ID,
c.InspectorID,
c.DisciplineID
FROM
dbo.TableC c
JOIN ( select
a.DisciplineID
from
TableA a
group by
a.DisciplineID
having
sum( case when a.ParentID = 0 then 1 else 0 end ) > 0
AND sum( case when a.ParentID > 0 then 1 else 0 end ) = 0 ) qual
on c.DisciplineID = qual.DisciplineID
You can try this:
SELECT DISTINCT B.INSPECTORID FROM TABLEA A
LEFT JOIN TABLEC CHILD ON CHILD.DISCIPLINEID = A.DISCIPLINEID
LEFT JOIN TABLEC PARENT ON PARENT.DISCIPLINEID = A.PARENTID
JOIN TABLEB B ON A.INSPECTORID = B.INSPECTORID
WHERE (A.PARENTID = 0 AND CHILD.DISCIPLINEID IS NOT NULL)

Is there a simpler way to write this query? [MS SQL Server]

I'm wondering if there is a simpler way to accomplish my goal than what I've come up with.
I am returning a specific attribute that applies to an object. The objects go through multiple iterations and the attributes might change slightly from iteration to iteration. The iteration will only be added to the table if the attribute changes. So the most recent iteration might not be in the table.
Each attribute is uniquely identified by a combination of the Attribute ID (AttribId) and Generation ID (GenId).
Object_Table
ObjectId | AttribId | GenId
32 | 2 | 3
33 | 3 | 1
Attribute_Table
AttribId | GenId | AttribDesc
1 | 1 | Text
2 | 1 | Some Text
2 | 2 | Some Different Text
3 | 1 | Other Text
When I query on a specific object I would like it to return an exact match if possible. For example, Object ID 33 would return "Other Text".
But if there is no exact match, I would like for the most recent generation (largest Gen ID) to be returned. For example, Object ID 32 would return "Some Different Text". Since there is no Attribute ID 2 from Gen 3, it uses the description from the most recent iteration of the Attribute which is Gen ID 2.
This is what I've come up with to accomplish that goal:
SELECT attr.AttribDesc
FROM Attribute_Table AS attr
JOIN Object_Table AS obj
ON obj.AttribId = obj.AttribId
WHERE attr.GenId = (SELECT MIN(GenId)
FROM(SELECT CASE obj2.GenId
WHEN attr2.GenId THEN attr2.GenId
ELSE(SELECT MAX(attr3.GenId)
FROM Attribute_Table AS attr3
JOIN Object_Table AS obj3
ON obj3.AttribId = attr3.AttribId
WHERE obj3.AttribId = 2
)
END AS GenId
FROM Attribute_Table AS attr2
JOIN Object_Table AS obj2
ON attr2.AttribId = obj2.AttribId
WHERE obj2.AttribId = 2
) AS ListOfGens
)
Is there a simpler way to accomplish this? I feel that there should be, but I'm relatively new to SQL and can't think of anything else.
Thanks!
The following query will return the matching value, if found, otherwise use a correlated subquery to return the value with the highest GenId and matching AttribId:
SELECT obj.Object_Id,
CASE WHEN attr1.AttribDesc IS NOT NULL THEN attr1.AttribDesc ELSE attr2.AttribDesc END AS AttribDesc
FROM Object_Table AS obj
LEFT JOIN Attribute_Table AS attr1
ON attr1.AttribId = obj.AttribId AND attr1.GenId = obj.GenId
LEFT JOIN Attribute_Table AS attr2
ON attr2.AttribId = obj.AttribId AND attr2.GenId = (
SELECT max(GenId)
FROM Attribute_Table AS attr3
WHERE attr3.AttribId = obj.AttribId)
In the case where there is no matching record at all with the given AttribId, it will return NULL. If you want to get no record at all in this case, make the second JOIN an INNER JOIN rather than a LEFT JOIN.
Try this...
Incase the logic doesn't find a match for the Object_table GENID it maps it to the next highest GENID in the ON clause of the JOIN.
SELECT AttribDesc
FROM object_TABLE A
INNER JOIN Attribute_Table B
ON A.AttrbId = B.AttrbId
AND (
CASE
WHEN A.Genid <> B.Genid
THEN (
SELECT MAX(C.Genid)
FROM Attribute_Table C
WHERE A.AttrbId = C.AttrbId
)
ELSE A.Genid
END
) -- Selecting the right GENID in the join clause should do the job
= B.Genid
This should work:
with x as (
select *, row_number() over (partition by AttribId order by GenId desc) as rn
from Attribute_Table
)
select isnull(a.attribdesc, x.attribdesc)
from Object_Table o
left join Attribute_Table a
on o.AttribId = a.AttribId and o.GenId = a.GenId
left join x on o.AttribId = x.AttribId and rn = 1

Using (IN operator) OR condition in Where clause as AND condition

Please look at following image, I have explained my requirements in the image.
alt text http://img30.imageshack.us/img30/5668/shippment.png
I can't use here WHERE UsageTypeid IN(1,2,3,4) because this will behave as an OR condition and fetch all records.
I just want those records, of first table, which are attached with all 4 ShipmentToID .
All others which are attached with 3 or less ShipmentToIDs are not needed in result set.
Thanks.
if (EntityId, UsageTypeId) is unique:
select s.PrimaryKeyField, s.ShipmentId from shipment s, item a
where s.PrimaryKeyField = a.EntityId and a.UsageTypeId in (1,2,3,4)
group by s.PrimaryKeyField, s.ShipmentId having count(*) = 4
otherwise, 4-way join for the 4 fields,
select distinct s.* from shipment s, item a, item b, item c, item d where
s.PrimaryKeyField = a.EntityId = b.EntityId = c.EntityId = d.EntityId and
a.UsageTypeId = 1 and b.UsageTypeId = 2 and c.UsageTypeId = 3 and
d.UsageTypeId = 4
you'll want appropriate index on (EntityId, UsageTypeId) so it doesn't hang...
If there will never be duplicates of the UsageTypeId-EntityId combo in the 2nd table, so you'll never see:
EntityUsageTypeId | EntityId | UsageTypeId
22685 | 4477 | 1
22687 | 4477 | 1
You can count matching EntityIds in that table.
WHERE (count(*) in <tablename> WHERE EntityId = 4477) = 4
DECLARE #numShippingMethods int;
SELECT #numShippingMethods = COUNT(*)
FROM shippedToTable;
SELECT tbl1.shipmentID, COUNT(UsageTypeId) as Usages
FROM tbl2 JOIN tbl1 ON tbl2.EntityId = tbl1.EntityId
GROUP BY tbl1.EntityID
HAVING COUNT(UsageTypeId) = #numShippingMethods
This way is preferred to the multiple join against same table method, as you can simply modify the IN clause and the COUNT without needing to add or subtract more tables to the query when your list of IDs changes:
select EntityId, ShipmentId
from (
select EntityId
from (
select EntityId
from EntityUsage eu
where UsageTypeId in (1,2,3,4)
group by EntityId, UsageTypeId
) b
group by EntityId
having count(*) = 4
) a
inner join Shipment s on a.EntityId = s.EntityId