SQL QUERY Select employee who under a specific boss - sql

A challenging question, i have table like below
EmployeeID BossID
pic http://img16.imageshack.us/img16/7659/20130430113245.jpg
any idea to create the query so that when certain employee go in , it will query all employee under him and who is his boss

Try this query
You have to use CTE as Sohail has mentioned.
WITH DirectReports (bossId, EmpID, Level)
AS
(
-- Anchor member definition
SELECT bossId, empId,
0 AS Level
FROM tbl
WHERE empId = 2
UNION ALL
-- Recursive member definition
SELECT e.bossId, e.empId,
Level + 1 AS Level
FROM tbl e
INNER JOIN DirectReports AS d
ON e.bossId = d.empId
)
-- Statement that executes the CTE
SELECT *
FROM DirectReports;
SQL FIDDLE
| BOSSID | EMPID | LEVEL |
--------------------------
| 1 | 2 | 0 |
| 2 | 4 | 1 |
| 4 | 5 | 2 |
| 4 | 6 | 2 |

You should create the query using the CTE(Common table expression). For help you can read Recursive Queries
Below is just similar example you can modify as per your need but it would help.
This is a table structure.
CREATE TABLE [dbo].[Categories](
[Id] [bigint] IDENTITY(1,1) NOT NULL,
[CategoryName] varchar NULL,
[ParentId] [bigint] NULL)
This is the hierarchical query. I just created a view for it because I need to filter it more in my scenario.
CREATE VIEW [dbo].[CategoriesWithNameHierarchy] AS WITH Categories_Tree AS
(SELECT c.id AS 'Id' ,
0 AS 'Level',
c.CategoryName AS 'CategoryName',
cast(c.CategoryName AS varchar(30)) AS 'CNameHierarchy'
FROM Categories c
WHERE ParentId IS NULL
UNION ALL SELECT ChildCategories.Id AS 'Id',
(1 + ct.[Level]) AS 'Level',
ChildCategories.CategoryName AS 'CategoryName',
cast(ct.CNameHierarchy + '>' + ChildCategories.CategoryName AS varchar(30)) AS 'CNameHierarchy'
FROM Categories ChildCategories,
Categories_Tree ct
WHERE ChildCategories.ParentId = ct.Id)
SELECT *
FROM Categories_Tree
Hope this would help.

Related

SQL query to get list of all records that are placed higher in hierarchy

Table:
+-----+------------+-------------+
| Id | DocumentNo | ParentCCID |
+-----+------------+-------------+
| 10 | CC001 | NULL |
| 20 | CC002 | CC001 |
| 33 | CC003 | CC002 |
+-----+-------------+-------------+
Value passed to the query: CC003
Expected Output:
CC003
CC002
CC001
Failed Attempt:
select b2.documentno,b2.ParentCCID from basicdetails b1
inner join basicdetails b2 on b1.documentno = b2.ParentCCID
where b2.documentno='CC003'
Note: DocumentNo is unique primary key. ParentCCID could have null values if there is no parent record.
EDIT:
create table basicdetails2
(
id int identity,
documentno varchar(30),
parentccid varchar(30)
)
insert into basicdetails2 values('CC001', null)
insert into basicdetails2 values('CC002', 'CC001')
insert into basicdetails2 values('CC003', 'CC002')
insert into basicdetails2 values('CC004', 'CC003')
You want a recursive cte:
with cte as (
select bd.documentno, bd.ParentCCID
from basicdetails bd
where bd.documentno = 'CC003'
union all
select cte.documentno, cte.ParentCCID
from cte join
basicdetails bd
on bd.documentno = cte.ParentCCID
)
select bd.documentno
from cte;
Just a minor twist on Gordon's answer (already +1).
I like to track the level and see the parents for each record
Example
Declare #Fetch varchar(25) = 'CC003'
;with cte as (
Select DocumentNo
,ParentCCDocumentNo
,Lvl=1
From YourTable
Where DocumentNo=#Fetch
Union All
Select R.DocumentNo
,R.ParentCCDocumentNo
,P.Lvl+1
From YourTable R
Join cte P on P.ParentCCDocumentNo = R.DocumentNo)
Select Lvl = Row_Number() over (Order By Lvl Desc)
,DocumentNo
,ParentCCDocumentNo
From cte
Order By 1 desc
Returns
Lvl DocumentNo ParentCCDocumentNo
3 CC003 CC002
2 CC002 CC001
1 CC001 NULL

Recursive delete of multiple rows in one table

I have a problem with my postgres database. I have a table Tasks with 3 columns: ID, Name and Parent_ID (which refers to another task id in this table):
id | name | parent_id
---+------+-----------
1 | A | 0
2 | B | 1
3 | C | 2
4 | D | 1
5 | E | 0
6 | F | 0
So basically it's like this:
1. A
2. B
3. C
4. D
5. E
6. F
What I'm trying to do is to delete task A, and delete all of its children and all children of children etc etc..(in this case B and D, along with C as its children of B which is deleted) something like cascade delete, but i cant do this. Maybe any function will work?
The result after delete should be
id | name | parent_id
---+------+-----------
5 | E | 0
6 | F | 0
Hope you guys can help me.
You only need a cascading FK-constraint:
\i tmp.sql
CREATE TABLE employees (
id serial PRIMARY KEY
, name VARCHAR NOT NULL
, parent_id INT REFERENCES employees(id) ON DELETE CASCADE
);
INSERT INTO employees
VALUES (1,'A', NULL),(2,'B', 1),(3,'C', 2),
(4,'D', 1),(5,'E', NULL),(6,'F', NULL);
DELETE FROM employees
WHERE name = 'A'
;
SELECT * FROM employees
;
----------
Result:
----------
DROP SCHEMA
CREATE SCHEMA
SET
CREATE TABLE
INSERT 0 6
DELETE 1
id | name | parent_id
----+------+-----------
5 | E |
6 | F |
(2 rows)
You can build the list of all descendent rows from the starting name with a recursive query, and then use it as a filter for the deletetions.
with recursive cte(id, parent_id) as (
select id, parent_id from mytable where name = 'A'
union all
select t.id, t.parent_id from mytable t inner join cte c on c.id = t.parent_id
)
delete from mytable where id in (select id from cte)
Demo on DB Fiddle - table content after executing the query:
id | name | parent_id
-: | :--- | --------:
5 | E | 0
6 | F | 0
Use recursive cte to get all rows with Name = 'A' and it's subordinates.
Then delete it from table employees.
Here is the step to create the table:
Sample table: CREATE TABLE employees (
id serial PRIMARY KEY,
name VARCHAR NOT NULL,
parent_id INT
);
INSERT INTO employees
VALUES (1,'A', 0),(2,'B', 1),(3,'C', 2),
(4,'D', 1),(5,'E', 0),(6,'F', 0);
Query:
WITH RECURSIVE subordinates AS (
SELECT
id,
parent_id,
name
FROM
employees
WHERE
name = 'A'
UNION
SELECT
e.id,
e.parent_id,
e.name
FROM
employees e
INNER JOIN subordinates s ON s.id = e.parent_id
)
DELETE
FROM employees
WHERE id in (
SELECT
id
FROM
subordinates);
SELECT * FROM employees;

How to get detail table records as string in SQL Server

I have a table in database which contains hospital names as follow:
+------------+--------------+
| HospitalID | HospitalName |
+------------+--------------+
| 1 | Hosp1 |
| 2 | Hosp2 |
| 3 | Hosp3 |
| 4 | Hosp4 |
+------------+--------------+
Another table also exist which contains activity names as follows:
+------------+--------------+
| ActivityID | ActivityName |
+------------+--------------+
| 1 | Act1 |
| 2 | Act2 |
| 3 | Act3 |
| 4 | Act4 |
| 5 | Act5 |
+------------+--------------+
There's a N*M relation between these tables, i.e. each hospital can operate different activities. Therefore another table is required as follows:
+----+------------+------------+
| ID | HospitalID | ActivityID |
+----+------------+------------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 1 | 5 |
| 4 | 2 | 1 |
| 5 | 2 | 3 |
| 6 | 3 | 2 |
+----+------------+------------+
I want to write a select statement which selects hospital names and their related activities in a string field as follows:
+--------------+------------------+
| HospitalName | ActivityNames |
+--------------+------------------+
| Hosp1 | Act1, Act2, Act5 |
| Hosp2 | Act1, Act3 |
| Hosp3 | Act2 |
| Hosp4 | |
+--------------+------------------+
I have written the select statement using a function for ActivityNames field using a cursor but it is not optimized and the system performance decreases as the number of records increases.
Any solution or suggestion on how to solve this problem?
You can do this just with a select. No need of looping or Cursor for this. Looping will make performance degrade.
So the Schema will be
CREATE TABLE #HOSPITAL( HOSPITALID INT, HOSPITALNAME VARCHAR(20))
INSERT INTO #HOSPITAL
SELECT 1, 'HOSP1'
UNION ALL
SELECT 2 , 'HOSP2'
UNION ALL
SELECT 3 ,'HOSP3'
UNION ALL
SELECT 4 , 'HOSP4'
CREATE TABLE #ACTIVITY( ActivityID INT, ActivityName VARCHAR(50) )
INSERT INTO #ACTIVITY
SELECT 1, 'Act1'
UNION ALL
SELECT 2, 'Act2'
UNION ALL
SELECT 3, 'Act3'
UNION ALL
SELECT 4, 'Act4'
UNION ALL
SELECT 5, 'Act5'
CREATE TABLE #HOSPITAL_ACT_MAP(ID INT, HospitalID INT, ActivityID INT)
INSERT INTO #HOSPITAL_ACT_MAP
SELECT 1, 1, 1
UNION ALL
SELECT 2, 1, 2
UNION ALL
SELECT 3, 1, 5
UNION ALL
SELECT 4, 2, 1
UNION ALL
SELECT 5, 2, 3
UNION ALL
SELECT 6, 3, 2
And do Select like below with CTE
;WITH CTE AS (
SELECT DISTINCT H.HOSPITALNAME, A.ActivityName FROM #HOSPITAL_ACT_MAP HA
INNER JOIN #HOSPITAL H ON HA.HospitalID = H.HOSPITALID
INNER JOIN #ACTIVITY A ON HA.ActivityID = A.ActivityID
)
SELECT HOSPITALNAME
, (SELECT STUFF((SELECT ','+ActivityName FROM CTE C1
WHERE C1.HOSPITALNAME = C.HOSPITALNAME
FOR XML PATH('')),1,1,''))
FROM CTE C
GROUP BY HOSPITALNAME
Edit from Comments
If you can't use CTE and Stuff go for Method 2
DECLARE #TAB TABLE (HOSPITALNAME VARCHAR(20),ActivityName VARCHAR(20) )
INSERT INTO #TAB
SELECT DISTINCT H.HOSPITALNAME, A.ActivityName FROM #HOSPITAL_ACT_MAP HA
INNER JOIN #HOSPITAL H ON HA.HospitalID = H.HOSPITALID
INNER JOIN #ACTIVITY A ON HA.ActivityID = A.ActivityID
SELECT HOSPITALNAME, SUBSTRING(ACTIVITIES,1, LEN(ACTIVITIES)-1) FROM(
SELECT DISTINCT HOSPITALNAME,(SELECT ActivityName+',' FROM #TAB T1
WHERE T1.HOSPITALNAME = T.HOSPITALNAME
FOR XML PATH('') ) AS ACTIVITIES FROM #TAB T
)A
Note: For the performance purpose I have stored the Intermediate result on #TAB (Table variable). If you want you can directly Query it with Sub Query.
using STUFF function to achieve your result :
CREATE TABLE #Hospital(HospitalID INT,HospitalName VARCHAR(100))
CREATE TABLE #Activity(ActivityID INT,ActivityName VARCHAR(100))
CREATE TABLE #RelationShip(Id INT,HospId INT,ActId INT)
CREATE TABLE #ConCat(HospitalID INT ,HospName VARCHAR(100), ActName
VARCHAR(100),UpFlag TINYINT DEFAULT(0))
DECLARE #HospId INT = 0,#String VARCHAR(200) = ''
INSERT INTO #Hospital(HospitalID ,HospitalName )
SELECT 1,'Hosp1' UNION ALL
SELECT 2,'Hosp2' UNION ALL
SELECT 3,'Hosp3' UNION ALL
SELECT 4,'Hosp4'
INSERT INTO #Activity(ActivityID ,ActivityName )
SELECT 1,'Act1' UNION ALL
SELECT 2,'Act2' UNION ALL
SELECT 3,'Act3' UNION ALL
SELECT 4,'Act4' UNION ALL
SELECT 5,'Act5'
INSERT INTO #RelationShip(ID,HospId,ActId)
SELECT 1 , 1 , 1 UNION ALL
SELECT 2 , 1 , 2 UNION ALL
SELECT 3 , 1 , 5 UNION ALL
SELECT 4 , 2 , 1 UNION ALL
SELECT 5 , 2 , 3 UNION ALL
SELECT 6 , 3 , 2
SELECT HospitalName , STUFF( ( SELECT ',' + ActivityName FROM #Activity
JOIN #RelationShip ON ActId = ActivityID WHERE HospId = HospitalID FOR XML
PATH('') ),1,1,'')
FROM #Hospital
GROUP BY HospitalID,HospitalName
***FOR SQLServer2005 Use below code***
INSERT INTO #ConCat (HospitalID ,HospName)
SELECT DISTINCT HospitalID ,HospitalName
FROM #Hospital
WHILE EXISTS(SELECT 1 FROM #ConCat WHERE UpFlag = 0)
BEGIN
SELECT #HospId = HospitalID FROM #ConCat WHERE UpFlag = 0 ORDER BY
HospitalID
SET #String = ''
SELECT #String = ISNULL(#String,'') + CAST(A.ActivityName AS VARCHAR) +
',' FROM
(
SELECT ActivityName
FROM #RelationShip
JOIN #Activity ON ActId = ActivityID
WHERE HospId = #HospId
) A
UPDATE #ConCat SET UpFlag = 1,ActName = CASE WHEN #String = '' THEN
#String ELSE SUBSTRING(#String,0,LEN(#String) ) END WHERE HospitalID
= #HospId
END
SELECT * FROM #ConCat
You can use json to increase the performance of front end.
There is no particular solution if you are using open source databases.
Try to use IBM db2 or ORACLE database to ensure performance of your app.
then generate json data . You will find the improvement in speed

Counting Policy Ages with a Query in tsql

I have a table containing insurance policies (let's call it POLICIES) in one field, along with the policies off of which they were renewed in another field:
POLICY_ID | PRIOR_POLICY_ID
===========================
ABC |
ABD | ABC
AFP |
ANR | ABD
BRC | AFP
FKZ |
I would like to write a query to count the total number of prior policies for each policy, with the result looking like this:
POLICY_ID | NUM_PRIOR_POLICIES
==============================
ABC | 0
ABD | 1
AFP | 0
ANR | 2
BRC | 1
FKZ | 0
Any suggestions would be appreciated.
You need a recursive CTE for this:
with cte as (
select p.policy_id, 0 as num_priors
from policies p
where prior_policy_id is null
union all
select p.policy_id, 1 + cte.num_priors
from cte join
policies p
on p.prior_policy_id = cte.policy_id
)
select *
from cte;
Here is a SQL Fiddle showing it working.
DECLARE #data TABLE ( POLICY_ID char(3), PRIOR_POLICY_ID char(3) );
INSERT #data VALUES
('ABC',NULL ),('ABD','ABC'),('AFP',NULL ),
('ANR','ABD'),('BRC','AFP'),('FKZ',NULL );
WITH cte AS (
SELECT POLICY_ID, 0 AS NUM_PRIOR_POLICIES
FROM #data
WHERE PRIOR_POLICY_ID IS NULL
UNION ALL
SELECT d.POLICY_ID, NUM_PRIOR_POLICIES + 1
FROM cte c
INNER JOIN #data d
ON (c.POLICY_ID = d.PRIOR_POLICY_ID)
)
SELECT POLICY_ID, NUM_PRIOR_POLICIES
FROM cte
ORDER BY POLICY_ID

Getting the Next Available Row

How can I get a List all the JobPositionNames having the lowest jobPositionId when ContactId = 1
Tablel :
| JobPositionId | JobPositionName | JobDescriptionId | JobCategoryId | ContactId
---------------------------------------------------------------------------------
1 | Audio Cables | 1 | 1 | 1
2 |Audio Connections| 2 | 1 | 1
3 |Audio Connections| 2 | 1 | 0
4 |Audio Connections| 2 | 1 | 0
5 | Sound Board | 3 | 1 | 0
6 | Tent Pen | 4 | 3 | 0
eg the result of this table should be lines 1,3,5,6
I can't figure out the solution.
Only lack of something, but I can give some code for you view.
Maybe it can help you.
--create table
create table t
(
JobPositionId int identity(1,1) primary key,
JobPositionName nvarchar(100) not null,
JobDescriptionId int,
JobCategoryId int,
ContactId int
)
go
--insert values
BEGIN TRAN
INSERT INTO t VALUES ('AudioCables', 1,1,1)
INSERT INTO t VALUES ('AudioConnections',2,1,1)
INSERT INTO t VALUES ('AudioConnections',2,1,0)
INSERT INTO t VALUES ('AudioConnections',2,1,0)
INSERT INTO t VALUES ('SoundBoard',3,1,0)
INSERT INTO t VALUES ('TentPen',4,3,0)
COMMIT TRAN
GO
SELECT
Min(JobPositionId) AS JobPositionId, JobPositionName, ContactId
INTO
#tempTable
FROM
t
GROUP BY JobPositionName, ContactId
SELECT * FROM #tempTable
WHERE JobPositionId IN (
SELECT JobPositionId
FROM #tempTable
GROUP BY JobPositionName
--... lack of sth, I can't figure out ,sorry.
)
drop table t
GO
For per-group maximum/minimum queries you can use a null-self-join as well as strategies like subselects. This is generally faster in MySQL.
SELECT j0.JobPositionId, j0.JobPositionName, j0.ContactId
FROM Jobs AS j0
LEFT JOIN Jobs AS j1 ON j1.JobPositionName=j0.JobPositionName
AND (
(j1.ContactId<>0)<(j0.ContactId<>0)
OR ((j1.ContactId<>0)=(j0.ContactId<>0) AND j1.JobPositionId<j0.JobPositionId))
)
WHERE j1.JobPositionName IS NULL
This says, for each JobPositionName, find a row for which there exists no other row with a lower ordering value. The ordering value here is a composite [ContactId-non-zeroness, JobPositionId].
(Aside: shouldn't JobPositionName and JobCategoryId be normalised out into a table keyed on JobDescriptionId? And shouldn't unassigned ContactIds be NULL?)
SELECT jp.*
FROM (
SELECT JobPositionName, JobPositionId, COUNT(*) AS cnt
FROM JobPosisions
) jpd
JOIN JobPosisions jp
ON jp.JobPositionId =
IF(
cnt = 1,
jpd.JobPositionId,
(
SELECT MIN(JobPositionId)
FROM JobPositions jpi
WHERE jpi.JobPositionName = jpd.JobPositionName
AND jpi.ContactID = 0
)
)
Create an index on (JobPositionName, ContactId, JobPositionId) for this to work fast.
Note that if will not return the jobs having more than one position, neither of which has ContactID = 0