WITH clause within EA Sparx query - enterprise-architect

Is it possible to use WITH clauses within an EA Sparx query ?
I wrote this one :
with Activities as
(
select t_connector.start_object_id as Object2_id, t_object.* from t_object
join t_connector on t_connector.end_object_id=t_object.Object_id
where t_object.Object_type= 'Activity'
union
select t_connector.end_object_id as Object2_id, t_object.* from t_object
join t_connector on t_connector.start_object_id=t_object.Object_id
where t_object.Object_type= 'Activity'
)
select * from
(
select obj.object_id, count(other.object_id) as 'Activities' from
t_object obj left join Activities as other on obj.Object_id=other.Object2_id
group by obj.object_id
) as ac
where object_id in (143306, 143321, 143226, 143326)
and it doesn't return any rows nor throw any errors, while the same query with the WITH-statement placed inline returns the expected results:
select * from
(
select obj.object_id, count(other.object_id) as 'Activities' from
t_object obj left join
(
select t_connector.start_object_id as Object2_id, t_object.* from t_object
join t_connector on t_connector.end_object_id=t_object.Object_id
where t_object.Object_type= 'Activity'
union
select t_connector.end_object_id as Object2_id, t_object.* from t_object
join t_connector on t_connector.start_object_id=t_object.Object_id
where t_object.Object_type= 'Activity'
)
as other on obj.Object_id=other.Object2_id
group by obj.object_id
) as ac
where object_id in (143306, 143321, 143226, 143326)
I know that the SQL engine of EA is a bit tricky (e.g. the query cannot start by a comment).
Is there some tricks here to let WITH clauses working in EA ?

EA does some basic checks on the queries. One of them is to check that the query starts with the keyword select.
So, no there is no way to get EA to execute a search query starting with something else then select.
In many circumstances however the WITH clause is used to perform recursive queries e.g. get all elements in a package and all it's subpackages.
For this specific case you can use the macro #Branch# which will be translated to a comma separated list of packageID's of the currently selected package and all of it's subpackages recursively.

Related

Exclude subtypes from SQL Query

I have the Linq query below:
var result = (from booking in _context.Booking.AsNoTracking()
join vendor in _context.Vendor.AsNoTracking() on booking.VendorId equals vendor.Id
where
booking.ClientId == userId
orderby booking.Id descending
select vendor.TypeId).FirstOrDefault();
A Vendor can be subtyped into a Mobile or a Garage.
But for my use case, I don't care about the subtype.
The SQL that is generated includes the subtype tables with a UNION.
exec sp_executesql N'SELECT TOP (1)
[Project3].[TypeId] AS [TypeId]
FROM ( SELECT
[Extent1].[Id] AS [Id],
[Join1].[TypeId] AS [TypeId]
FROM [dbo].[Booking] AS [Extent1]
INNER JOIN (SELECT [UnionAll1].[VendorId] AS [VendorId], [Extent4].[TypeId] AS [TypeId]
FROM (SELECT
[Extent2].[VendorId] AS [VendorId]
FROM [dbo].[Garage] AS [Extent2]
UNION ALL
SELECT
[Extent3].[VendorId] AS [VendorId]
FROM [dbo].[Mobile] AS [Extent3]) AS [UnionAll1]
INNER JOIN [dbo].[Vendor] AS [Extent4] ON [UnionAll1].[VendorId] = [Extent4].[Id] ) AS [Join1] ON [Extent1].[VendorId] = [Join1].[VendorId]
WHERE [Extent1].[ClientId] = #p__linq__0
) AS [Project3]
ORDER BY [Project3].[Id] DESC',N'#p__linq__0 uniqueidentifier',#p__linq__0='6AE9C275-7944-47B0-9B03-7B10EC88C98C'
This is wasteful.
How can I exclude the SubTypes from being included in the SQL command?
Bonus question.
Can I make the TOP 1 run in the nested SQL?
Again. This is wasteful. It gets a full result first, and then TOP 1s it.

select table.object_type and select table.* don't return the same value for object_type

I'm trying to select all the 'Application Functions' linked to an object. So I've read the following query:
select
other.ea_guid as CLASSGUID, other.object_type as CLASSTYPE,
obj.name,
other.*
from t_object as obj
join (
select t_connector.start_object_id as Object2_id, t_object.* from t_object
join t_connector on t_connector.end_object_id=t_object.Object_id
where t_object.object_type = 'Application Function'
union
select t_connector.end_object_id as Object2_id, t_object.* from t_object
join t_connector on t_connector.start_object_id=t_object.Object_id
where t_object.object_type = 'Application Function'
) as other on obj.Object_id=other.Object2_id
where obj.object_id = 143299
And it is empty.
Without the where clause, it returns:
But if I change in the select the other.* by named columns like other.object_type, object_name then column obj.namegets empty and other.object_typeas another value:
I thing this difference explains why my select doesn't work. But how to explain and resolve this ?
EA does some special magic to some of the fields when returned by a query.
It does so based on the exact name of the returned field such as Object_Type or Note
To avoid this make sure to give those fields a different alias. e.g. Object_Type as theRealType
In this case I'm going to assume you are looking for ArchiMate application functions. The fact that this is an Application function is actually stored in the Stereotype rather than the Object_Type
A query like this should return all ArchiMate Application Functions linked to an object with id 143299
select o2.Object_ID AS CLASSGUID, o2.Object_Type AS CLASSTYPE, o2.Object_Type as theRealObjectType,
o2.*
from t_object o
inner join t_connector c on c.Connector_ID in (c.Start_Object_ID, c.End_Object_ID)
inner join t_object o2 on o2.Object_ID in (c.Start_Object_ID, c.End_Object_ID)
and o2.Object_ID <> o.Object_ID
where o.Stereotype = 'ArchiMate_ApplicationFunction'
and o2.Object_ID = 143299
This query has been written and tested on an SQL Server repository. It might need tweaking if you want to use it on a .eap file (MS Access SQL Syntax)

How to avoid duplicates in the STRING_AGG function

My query is below:
select
u.Id,
STRING_AGG(sf.Naziv, ', ') as 'Ustrojstvena jedinica',
ISNULL(CONVERT(varchar(200), (STRING_AGG(TRIM(p.Naziv), ', ')), 121), '')
as 'Partner',
from Ugovor as u
left join VezaUgovorPartner as vup
on vup.UgovorId = u.Id AND vup.IsDeleted = 'false'
left join [TEST_MaticniPodaci2].dbo.Partner as p
on p.PartnerID = vup.PartnerId
left join [dbo].[VezaUgovorUstrojstvenaJedinica] as vuu
on vuu.UgovorId = u.Id
left join [TEST_MaticniPodaci2].hcphs.SifZavod as sf
on sf.Id = vuu.UstrojstvenaJedinicaId
left join [dbo].[SifVrstaUgovora] as vu
on u.VrstaUgovoraId = vu.Id
group by u.Id, sf.Naziv
My problem is that I can have more sf.Naziv and also only one sf.Naziv so I have to check if there is one and then show only one result and if there is two or more to show more results. But for now the problem is when I have only one sf.Naziv, query returns two sf.Naziv with the same name because in first STRING_AGG i have more records about p.Naziv.
I have no idea how to implement DISTINCT into STRING_AGG function
Any other solutions are welcome, but I think it should work with DISTINCT function.
It looks like distinct won't work, so what you should do is put your whole query in a subquery, remove the duplicates there, then do STRING_AGG on the data that has no duplicates.
SELECT STRING_AGG(data)
FROM (
SELECT DISTINCT FROM ...
)
I like this format for distinct values:
(d is required but you can use any variable name there)
SELECT STRING_AGG(LoadNumber, ',') as LoadNumbers FROM (SELECT DISTINCT LoadNumber FROM [ASN]) d
A sample query to remove duplicates while using STRING_AGG().
WITH cte AS (
SELECT DISTINCT product
FROM activities
)
SELECT STRING_AGG(product, ',') products
FROM cte;
Or you can use the following query. The result is same -
SELECT STRING_AGG(product, ',') as products
from (
SELECT product
FROM Activities
GROUP BY product
) as _ ;

How to design a SQL recursive query?

How would I redesign the below query so that it will recursively loop through entire tree to return all descendants from root to leaves? (I'm using SSMS 2008). We have a President at the root. under him are the VPs, then upper management, etc., on down the line. I need to return the names and titles of each. But this query shouldn't be hard-coded; I need to be able to run this for any selected employee, not just the president. This query below is the hard-coded approach.
select P.staff_name [Level1],
P.job_title [Level1 Title],
Q.license_number [License 1],
E.staff_name [Level2],
E.job_title [Level2 Title],
G.staff_name [Level3],
G.job_title [Level3 Title]
from staff_view A
left join staff_site_link_expanded_view P on P.people_id = A.people_id
left join staff_site_link_expanded_view E on E.people_id = C.people_id
left join staff_site_link_expanded_view G on G.people_id = F.people_id
left join facility_view Q on Q.group_profile_id = P.group_profile_id
Thank you, this was most closely matching what I needed. Here is my CTE query below:
with Employee_Hierarchy (staff_name, job_title, id_number, billing_staff_credentials_code, site_name, group_profile_id, license_number, region_description, people_id)
as
(
select C.staff_name, C.job_title, C.id_number, C.billing_staff_credentials_code, C.site_name, C.group_profile_id, Q.license_number, R.region_description, A.people_id
from staff_view A
left join staff_site_link_expanded_view C on C.people_id = A.people_id
left join facility_view Q on Q.group_profile_id = C.group_profile_id
left join regions R on R.regions_id = Q.regions_id
where A.last_name = 'kromer'
)
select C.staff_name, C.job_title, C.id_number, C.billing_staff_credentials_code, C.site_name, C.group_profile_id, Q.license_number, R.region_description, A.people_id
from staff_view A
left join staff_site_link_expanded_view C on C.people_id = A.people_id
left join facility_view Q on Q.group_profile_id = C.group_profile_id
left join regions R on R.regions_id = Q.regions_id
WHERE C.STAFF_NAME IS NOT NULL
GROUP BY C.STAFF_NAME, C.job_title, C.id_number, C.billing_staff_credentials_code, C.site_name, C.group_profile_id, Q.license_number, R.region_description, A.people_id
ORDER BY C.STAFF_NAME
But I am wondering what is the purpose of the "Employee_Hierarchy"? When I replaced "staff_view" in the outer query with "Employee_Hierarchy", it only returned one record = "Kromer". So when/where can we use "Employee_Hierarchy"?
See:
SQL Server - Simple example of a recursive CTE
MSDN: Recursive Queries using Common Table Expression
SQL Server recursive CTE (this seems pretty much like exactly what you are working on!)
Update:
A proper recursive CTE consist of basically three things:
an anchor SELECT to begin with; that can select e.g. the root level employees (where the Reports_To is NULL), or it can select any arbitrary employee that you define, e.g. by a parameter
a UNION ALL
a recursive SELECT statement that selects from the same, typically self-referencing table and joins with the recursive CTE being currently built up
This gives you the ability to recursively build up a result set that you can then select from.
If you look at the Northwind sample database, it has a table called Employees which is self-referencing: Employees.ReportsTo --> Employees.EmployeeID defines who reports to whom.
Your CTE would look something like this:
;WITH RecursiveCTE AS
(
-- anchor query; get the CEO
SELECT EmployeeID, FirstName, LastName, Title, 1 AS 'Level', ReportsTo
FROM dbo.Employees
WHERE ReportsTo IS NULL
UNION ALL
-- recursive part; select next Employees that have ReportsTo -> cte.EmployeeID
SELECT
e.EmployeeID, e.FirstName, e.LastName, e.Title,
cte.Level + 1 AS 'Level', e.ReportsTo
FROM
dbo.Employees e
INNER JOIN
RecursiveCTE cte ON e.ReportsTo = cte.EmployeeID
)
SELECT *
FROM RecursiveCTE
ORDER BY Level, LastName
I don't know if you can translate your sample to a proper recursive CTE - but that's basically the gist of it: anchor query, UNION ALL, recursive query

How to get the top most parent in PostgreSQL

I have a tree structure table with columns:
id,parent,name.
Given a tree A->B->C,
how could i get the most top parent A's ID according to C's ID?
Especially how to write SQL with "with recursive"?
Thanks!
WITH RECURSIVE q AS
(
SELECT m
FROM mytable m
WHERE id = 'C'
UNION ALL
SELECT m
FROM q
JOIN mytable m
ON m.id = q.parent
)
SELECT (m).*
FROM q
WHERE (m).parent IS NULL
To implement recursive queries, you need a Common Table Expression (CTE).
This query computes ancestors of all parent nodes. Since we want just the top level, we select where level=0.
WITH RECURSIVE Ancestors AS
(
SELECT id, parent, 0 AS level FROM YourTable WHERE parent IS NULL
UNION ALL
SELECT child.id, child.parent, level+1 FROM YourTable child INNER JOIN
Ancestors p ON p.id=child.parent
)
SELECT * FROM Ancestors WHERE a.level=0 AND a.id=C
If you want to fetch all your data, then use an inner join on the id, e.g.
SELECT YourTable.* FROM Ancestors a WHERE a.level=0 AND a.id=C
INNER JOIN YourTable ON YourTable.id = a.id
Assuming a table named "organization" with properties id, name, and parent_organization_id, here is what worked for me to get a list that included top level and parent level org ID's for each level.
WITH RECURSIVE orgs AS (
SELECT
o.id as top_org_id
,null::bigint as parent_org_id
,o.id as org_id
,o.name
,0 AS relative_depth
FROM organization o
UNION
SELECT
allorgs.top_org_id
,childorg.parent_organization_id
,childorg.id
,childorg.name
,allorgs.relative_depth + 1
FROM organization childorg
INNER JOIN orgs allorgs ON allorgs.org_id = childorg.parent_organization_id
) SELECT
*
FROM
orgs order by 1,5;