Sql recursive query to create a unique list - performance

I need to move some code from C# into a Stored Procedure for speed reasons. What I'm trying to get is a unique list of TemplateIds from the RoleTemplates (or CategoryToRoleTemplate) table based on a CategoryId.
However, I need the query to walk the Category.ParentId relationship, and collection all of the parent's related TemplateIds. This needs to happen until the ParentId is null.
Ideally the result should be a unique list of RoleTemplate.TemplateIds.
Table structure...
Categories
------------------------------
CategoryId uniqueidentifier
ParentId uniqueidentifier <-- Relationship to Categories.CategoryId.
Name varchar (50)
CategoryToRoleTemplate
------------------------------
CategoryId uniqueidentifier <-- Relationship to Categories.CategoryId.
TemplateId uniqueidentifier <-- Relationship to RoleTemplates.TemplateId.
RoleTemplates
------------------------------
TemplateId uniqueidentifier
Name varchar (50)
I'm using SQL Server 2008 R2.
Thanks!
EDIT:
Final solution:
with CategoryHierarchy (ParentId)
as (
-- Anchor member definition
select CategoryId from Categories
where CategoryId = #id
union all
-- Recursive member definition
(select c.ParentId from Categories as c
inner join CategoryHierarchy as p
on c.CategoryId = p.ParentId)
)
select distinct TemplateId from CategoryToRoleTemplates where CategoryId in (select CategoryId from CategoryHierarchy);
Thanks to all who answered! CTEs were the key.

I would suggest a CTE for doing that query. Keep in mind though that the tree will actually START at null and go until exhausted.
Example (may or may not work OOB given your code):
; WITH CategoryTree(CategoryID, sorthelp) AS
(SELECT CategoryID, 0 FROM Categories WHERE ParentID IS NULL)
UNION ALL
(SELECT C.CategoryID, CT.sorthelp + 1 FROM Categories C INNER JOIN CategoryTree CT ON C.PARENTID = CT.CategoryID)
SELECT DISTINCT TemplateID FROM RoleTemplates WHERE CategoryID IN (SELECT CategoryID FROM CategoryTree)
Good Point(tm): Don't forget the semicolon before the WITH keyword.

Use a recursive common table expression:
http://msdn.microsoft.com/en-us/library/ms186243.aspx

Please check this link http://msdn.microsoft.com/en-us/library/ms186243.aspx
I would go first with the table Categories with the with syntax and after that join with the others tables.

I'm short on time at the moment, so I can't be specific, but I would look into Common Table Expressions, which I've used successfully in the past to implement recursion.

Related

Oracle select rows from a query which are not exist in another query

Let me explain the question.
I have two tables, which have 3 columns with same data tpyes. The 3 columns create a key/ID if you like, but the name of the columns are different in the tables.
Now I am creating queries with these 3 columns for both tables. I've managed to independently get these results
For example:
SELECT ID, FirstColumn, sum(SecondColumn)
FROM (SELECT ABC||DEF||GHI AS ID, FirstTable.*
FROM FirstTable
WHERE ThirdColumn = *1st condition*)
GROUP BY ID, FirstColumn
;
SELECT ID, SomeColumn, sum(AnotherColumn)
FROM (SELECT JKM||OPQ||RST AS ID, SecondTable.*
FROM SecondTable
WHERE AlsoSomeColumn = *2nd condition*)
GROUP BY ID, SomeColumn
;
So I make a very similar queries for two different tables. I know the results have a certain number of same rows with the ID attribute, the one I've just created in the queries. I need to check which rows in the result are not in the other query's result and vice versa.
Do I have to make temporary tables or views from the queries? Maybe join the two tables in a specific way and only run one query on them?
As a beginner I don't have any experience how to use results as an input for the next query. I'm interested what is the cleanest, most elegant way to do this.
No, you most probably don't need any "temporary" tables. WITH factoring clause would help.
Here's an example:
with
first_query as
(select id, first_column, ...
from (select ABC||DEF||GHI as id, ...)
),
second_query as
(select id, some_column, ...
from (select JKM||OPQ||RST as id, ...)
)
select id from first_query
minus
select id from second_query;
For another result you'd just switch the tables, e.g.
with ... <the same as above>
select id from second_query
minus
select id from first_query

oracle select query based on other table row level condition

I want to find the orders number from table#orders where DelivaryDateRevision less than max revisions from each country(table#maxrevisions). Countrycode is not the foreign key to the other table.
Can I fetch the orders table records if the country code is missing in the maxrevisions table.
Table: orders
OrderNumber | CountryCode | DelivaryDateRevision
123--------------- IN-------------------9
234--------------- US-------------------3
238-------------- IN------------------ 3
table: maxrevisions
CountryCode| MaxRevision
IN ---------------6
US--------------- 4
My query:
SELECT distinct o.ordernumber,o.countrycode
FROM orders o
left outer join maxrevisions m
on o.CountryCode=m.CountryCode
and
o.DelivaryDateRevision<rs.MaxRevision;
but I am getting the wrong result. Can I get any help here?
Your major omission seems to be a WHERE clause which compares the two revisions:
SELECT
o.ordernumber,
o.countrycode
FROM orders o
LEFT JOIN maxrevisions m
ON o.CountryCode = m.CountryCode
WHERE
o.DelivaryDateRevision < m.MaxRevision OR m.MaxRevision IS NULL;
Demo
Select
ordernumber,
countrycode,
deliverydateversion
from orders o
where deliverydateversion >
(
select max(revision)
from maxrevisiontab
where countrycode= o.countrycode
)
Please change the table names and column names as per your structure.

SQL Query Performance with count

I have 2 tables, COMPANY and EMPLOYEE.
COMPANY_ID is the primary key of the COMPANY table and foreign key for EMPLOYEE table. The COMPANY_ID is a 10 digit number. We are generate a 3 number combination and query the database.
The select statement has regex to bulk load the company based on COMPANY_ID. The query is executed multiple times with different patterns
i.e.
regexp_like(COMPANY_ID, '^(000|001|002|003|004|005|006|007|008|009)') .
Existing query looks something like this -
select *
from COMPANY company
where regexp_like(company.COMPANY_ID, '^(000|001|002|003|004|005|006|007|008|009)')
The new requirement is to retrieve the company information along with the employee count. For example if a company has 10 employees, then the query should return all the columns of the COMPANY table, along with employee count i.e. 10
This is the select statement that I came up with -
select
nvl(count_table.cont_count, 0), company.*
from
COMPANY company,
(select company.COMPANY_ID, count(company.COMPANY_ID) as cont_count
from COMPANY company, EMPLOYEE employee
where regexp_like(company.COMPANY_ID, '^(000|001|002|003|004|005|006|007|008|009)')
and company.CONTACT_ID = employee.CONTACT_ID
group by (company.COMPANY_ID)) count_table
where
regexp_like(company.COMPANY_ID, '^(000|001|002|003|004|005|006|007|008|009)')
and count_table.COMPANY_ID(+)= company.COMPANY_ID
Above query works, but it takes double the time compared to the previous statement. Is there a better way to retrieve the employee count?
Note: Oracle database is in use.
You don't need to execute that expensive REGEXP_LIKE twice:
select nvl(count_table.cont_count,0),company.*
from COMPANY company
,( select employee.COMPANY_ID, count(employee.COMPANY_ID) as cont_count
from EMPLOYEE employee
group by (employee.COMPANY_ID)
) count_table
where regexp_like(company.COMPANY_ID, '^(000|001|002|003|004|005|006|007|008|009)')
and count_table.COMPANY_ID(+)= company.COMPANY_ID
Or you could use a scalar subquery:
select company.*
, (select count(*)
from employee e
where e.company_id = c.company_id
)
from COMPANY c
where regexp_like(c.COMPANY_ID, '^(000|001|002|003|004|005|006|007|008|009)')
And personally I would ditch the slow REGEXP_LIKE for something like:
where substr(c.company_id,1,3) between '000' and '009'
The derived table does not add value, thus I would get rid of it and use a scalar query (because I do not know all of your columns in the company table to properly do a group by):
select c.*,
nvl(
(select count(1)
from employee emp
where emp.company_id = c.company_id
),0) employee_count
from company c
where regexp_like(c.company_id, '^(000|001|002|003|004|005|006|007|008|009)')
Also, if performance is still an issue, I would consider modifying your where statement to not use a regexp.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Addendum
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
I see that the question explicitly identifies that the employee table has company_id as a foreign key. Since this is clarified, I am removing this statement:
The data model for these tables is not intuitive (would you not have
company_id as a foreign key in the employees table?).

Oracle SQL Query to get missing date from two joined tables

I'm trying to write a Oracle SQL query and I'm unable to get the right result.
For the tables below, I would like to first get all records from DEVICE where DEVICE.MODEL='UNITA' and for those results, give me the DEVICE.CUSTOMER_ID's who don't have a record where PROFILE.TYPE='TEST' joining both tables on CUSTOMER_ID. Any ideas on how to formulate this query?
TABLE DEVICE:
ID - sequence generated primary key (NUMBER (10))
DEVICE_NUMBER - unique (varchar)
CUSTOMER_ID (varchar)
MODEL (varchar)
TABLE PROFILE:
ID - sequence generated primary key (NUMBER (10))
CUSTOMER_ID (varchar)
TYPE (varchar)
If I understand the requirement, this should do the trick:
SELECT d.ID, d.Device_Number, d.Customer_ID, d.Model
FROM Device d
LEFT JOIN Profile p ON d.Customer_ID = p.Customer_ID
WHERE d.Model = 'UNITA'
AND (p.ID IS NULL OR p.Type = 'TEST')
It works because of the LEFT JOIN, which will make Profile.ID NULL if there's not a matching Profile row for the Customer_ID. If there is a matching row, the test for Profile.Type = 'TEST' will determine what's included.
There's a SQL Fiddle here. The Fiddle includes the Profile.ID and Profile.Type values in the results because I think they help explain things more clearly.
Addendum: Some confusion on my part over the requirements; this query may be closer to what's needed:
SELECT d.ID, d.Device_Number, d.Customer_ID, d.Model, p.id AS pid, p.type
FROM Device d
LEFT JOIN Profile p ON d.Customer_ID = p.Customer_ID AND p.Type = 'TEST'
WHERE d.Model = 'UNITA'
AND p.ID IS NULL

table subtraction challenge

I have a challenge that I haven’t overcome in the last two days using Stored Procedures and SQL 2008.
I took several approaches but must fell short.
One appraoch very interesting was using a table substraction.
It’s really all about table subtraction.
I was wondering if you could help me crack this one.
Here is the challenge:
Two tables 1Testdb y 2Testdb.
My first step was to select ID relationships ([2Testdb].Acc_id) on table 2Testdb for one given individual ([2Testdb].Bus_id). Then query table 1Testdb for records not mathcing my original selection from 2Testdb.
But other approaches are welcome.
Data and Structures:
USE [Challengedb]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[1Testdb](
[Acc_id] [uniqueidentifier] NULL
[Name] [Varchar(10)] NULL
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[2Testdb](
[Acc_id] [uniqueidentifier] NULL,
[Bus_id] [uniqueidentifier] NULL
) ON [PRIMARY]
GO
Records on 1Testdb:
34455F60-9474-4521-804E-66DB39A579F3, John
C23523F6-2309-4F58-BB3F-EF7486C7AF8B, Pete
DC711615-3BE4-4B31-9EF2-B1314185CA62, Dave
E3AAB073-2398-476D-828B-92829F686A4C, Adam
Records on 2Testdb: (Relationship table, ex. Friend relationships)
Record #1: DC711615-3BE4-4B31-9EF2-B1314185CA62, 34455F60-9474-4521-804E-66DB39A579F3
Record #2: E3AAB073-2398-476D-828B-92829F686A4C, 34455F60-9474-4521-804E-66DB39A579F3
Record # 3: DC711615-3BE4-4B31-9EF2-B1314185CA62, E3AAB073-2398-476D-828B-92829F686A4C
Record # 4: E3AAB073-2398-476D-828B-92829F686A4C, DC711615-3BE4-4B31-9EF2-B1314185CA62
Challenge: Select from table 1Testdb only those records distinct that may not have a relationship with John [34455F60-9474-4521-804E-66DB39A579F3] on table 2Testdb.
Expected result should be (Who does John doesn’t have relationship with?):
C23523F6-2309-4F58-BB3F-EF7486C7AF8B, Pete
Thank you,
Valentin
Not sure exactly what you're asking; is it for all users in the first table that don't have a friendship with "John" based on the second table?
If so, use the "not exists" keyword to determine whether or not a record exists with the given query:
select a.*
from [1testdb] a
where not exists (
select * from [2testdb] b where a.acc_id = b.acc_id and b.subid = '34455F60-9474-4521-804E-66DB39A579F3'
)
and a.acc_id <> '34455F60-9474-4521-804E-66DB39A579F3'
I'm not sure how your columns are set up...looks like GUIDs, but this is the SQL Server syntax for getting it to work. I included a case where John could be in either the Acc_id or Bus_id column, so that's why there are 2 joins instead of one.
Declare #id NVarchar(50)
Set #id = '34455F60-9474-4521-804E-66DB39A579F3'
Select *
From 1Testdb
Left Outer Join 2Testdb As ForwardRelationship On ForwardRelationship.Acc_id = #id And ForwardRelationship.Bus_id = 1Testdb.Acc_id
Left Outer Join 2Testdb As ReverseRelationship On ReverseRelationship.Bus_id = #id And ReverseRelationship.Acc_id = 1Testdb.Acc_id
Where
ForwardRelationship.Acc_id Is Null And
ForwardRelationship.Bus_id Is Null And
ReverseRelationship.Acc_id Is Null And
ReverseRelationship.Bus_id Is Null And
1Testdb.Acc_id <> #id

Resources