postgres SQL, function - postgresql

I want to create a function which returns how many levels the boss is above the person(calling the function).
Here is the way i would like to do it, but i don't quite know how SQL syntax works
http://pastebin.com/dyDaGwf9
the table looks like this:
workerid name chefid
1 Bob
2 Alice 1
3 Joe 1
4 John 2
5 Dirk 4
6 Ralf 2
7 Lisa 1
8 Lyn 3
the final result upon calling the function should look like this
function call:
Select workerid, name, rankFunction(workerid) from workers;
workerid name rank
1 Bob 0
2 Alice 1
3 Joe 1
4 John 2
5 Dirk 3
6 Ralf 2
7 Lisa 1
8 Lyn 2
Would be great if somebody could shed some light,
Thanks!

You don't need a function for this, just a recursive query (available as of version 8.4):
WITH RECURSIVE chef as (
SELECT workerid, name, chefid, 0 AS rank FROM workers WHERE chefid is null
UNION ALL
SELECT workers.workerid, workers.name, workers.chefid, rank + 1
FROM workers JOIN chef ON workers .chefid = chef.workerid
)
SELECT workerid, name, rank FROM chef ORDER BY workerid;

Here you are, a simple recursion:
CREATE OR REPLACE FUNCTION rankFunction(worker_id int)
RETURNS int AS
$BODY$
DECLARE
temp_chefid int;
BEGIN
temp_chefid := (SELECT chefid from workers where workerid = worker_id);
IF(temp_chefid IS NULL) THEN
RETURN 0;
ELSE RETURN 1 + rankFunction(temp_chefid);
END IF;
END;
$BODY$
LANGUAGE plpgsql VOLATILE

Related

Listing rows which have the same value as an entred id in a certain column in postgresql

I have a following table structure in Postgres:
id
conversation_id
member_id
3
2
73
4
2
1
5
2
2
6
3
1
8
3
73
How I can select all rows of members with the same conversation_id as the entred member_id (2) for example?
For the example above it will return rows 3, 4, 5
I tried this one query, but it does not return what I expect:
SELECT cu.id, cu.member_id
FROM conversation c, conversation_user cu
WHERE cu.conversation_id=c.id
GROUP BY cu.member_id, cu.id
having cu.member_id = 2
I found a solution:
SELECT cu.id
FROM conversation c, conversation_user cu
WHERE cu.conversation_id=c.id
AND cu.conversation_id = (SELECT cu2.conversation_id FROM conversation_user cu2 WHERE cu2.member_id=2 LIMIT 1)

How to write create table statement in stored procedure

How to write the below query in stored proc in postgresql?
create table data1 as
select A.*,
case when score >=940 then 1
when score between 600 and 746 then 2
when bureau_score between 599 and 630 then 4 else 5 end as score_level,
case when band between -1 and 5 then 1
when band between 6 and 20 then 2
when band between 21 and 35 then 3 else 4 end as band_level
from data A;
Postgresql doen't have stored procedures as such, only functions, so.
If it's simple SQL you can simply wrap in in an SQL function definition.
create or replace function foo () returns void language sql as $$
create table data1 as
select A.*,
case when score >=940 then 1
when score between 600 and 746 then 2
when bureau_score between 599 and 630 then 4 else 5 end as score_level,
case when band between -1 and 5 then 1
when band between 6 and 20 then 2
when band between 21 and 35 then 3 else 4 end as band_level
from data A;
$$;
To call it do SELECT foo();

SQL Select based on each row of previous select

I have a table with answers regarding different questions, all of them numbered. There are basically these columns: IdAnswer (unique for each answer in the table), IdUser (which won't repeat even if the same user answer questions a second time), IdQuestion and Answer.
IdAnswer IdUser IdQuestion Answer
1 John 1 0
2 John 4 1
3 John 5 1
4 John 6 0
5 Bob 1 1
6 Bob 3 1
7 Bob 5 0
8 Mark 2 0
9 Mark 7 1
10 Mark 5 0
I'd like to select from this table all answers to a specific question (say, IdQuestion = 5), and also the last question each user answered just before question number 5.
In the end I need a table that should look like this:
IdAnswer IdUser IdQuestion Answer
2 John 4 1
3 John 5 1
6 Bob 3 1
7 Bob 5 0
9 Mark 7 1
10 Mark 5 0
I've managed to make this work using a cursor to iterate through each line from the first SELECT result (which filters by IdQuestion), but I'm not sure if this is the best (and fastest) way of doing it. Is there any more efficient way of achieving the same result?
And by the way, I'm using SQL Server Management Studio 2012.
Here is one way using LEAD function
select * from
(
select *,NextQ = Lead(IdQuestion)over(partition by IdUser order by IdAnswer)
from youtable
) a
Where 5 in (IdQuestion, NextQ )
for older versions
;WITH cte
AS (SELECT prev_id = Min(CASE WHEN IdQuestion = 5 THEN rn - 1 END) OVER( partition BY IdUser),*
FROM (SELECT rn = Row_number()OVER(partition BY IdUser ORDER BY IdAnswer),*
FROM Yourtable)a)
SELECT *
FROM cte
WHERE rn IN ( prev_id, prev_id + 1 )

T-SQL How to get all items from a tree in T-SQL?

I have a problem with a t-sql query.
Let's say I have a categories tree (categories ID)
cat_table
1
|
2-\
| 3-\
6 | 5
| 4 |
... ...
ads_table
ad_ID
category_ID
of course the category_ID column references to the ID column in the cat_table
the problem is, how to get (recursive ?) all advertisements from all categories which the top-most parent is the 1st category?
try using a recursive Common Table Expressions, aka "CTE" (available in SQL Server 2005 and up) like this:
--go through a nested table supervisor - user table and display the chain
DECLARE #Contacts table (id varchar(6), first_name varchar(10), reports_to_id varchar(6))
INSERT #Contacts VALUES ('1','Jerome', NULL ) -- tree is as follows:
INSERT #Contacts VALUES ('2','Joe' ,'1') -- 1-Jerome
INSERT #Contacts VALUES ('3','Paul' ,'2') -- / \
INSERT #Contacts VALUES ('4','Jack' ,'3') -- 2-Joe 9-Bill
INSERT #Contacts VALUES ('5','Daniel','3') -- / \ \
INSERT #Contacts VALUES ('6','David' ,'2') -- 3-Paul 6-David 10-Sam
INSERT #Contacts VALUES ('7','Ian' ,'6') -- / \ / \
INSERT #Contacts VALUES ('8','Helen' ,'6') -- 4-Jack 5-Daniel 7-Ian 8-Helen
INSERT #Contacts VALUES ('9','Bill ' ,'1') --
INSERT #Contacts VALUES ('10','Sam' ,'9') --
DECLARE #Root_id char(4)
--get 2 and below
SET #Root_id=2
PRINT '#Root_id='+COALESCE(''''+#Root_id+'''','null')
;WITH StaffTree AS
(
SELECT
c.id, c.first_name, c.reports_to_id, c.reports_to_id as Manager_id, cc.first_name AS Manager_first_name, 1 AS LevelOf
FROM #Contacts c
LEFT OUTER JOIN #Contacts cc ON c.reports_to_id=cc.id
WHERE c.id=#Root_id OR (#Root_id IS NULL AND c.reports_to_id IS NULL)
UNION ALL
SELECT
s.id, s.first_name, s.reports_to_id, t.id, t.first_name, t.LevelOf+1
FROM StaffTree t
INNER JOIN #Contacts s ON t.id=s.reports_to_id
WHERE s.reports_to_id=#Root_id OR #Root_id IS NULL OR t.LevelOf>1
)
SELECT * FROM StaffTree
output:
#Root_id='2 '
id first_name reports_to_id Manager_id Manager_first_name LevelOf
------ ---------- ------------- ---------- ------------------ -----------
2 Joe 1 1 Jerome 1
3 Paul 2 2 Joe 2
6 David 2 2 Joe 2
7 Ian 6 6 David 3
8 Helen 6 6 David 3
4 Jack 3 3 Paul 3
5 Daniel 3 3 Paul 3
(7 row(s) affected)
change #Root_id to get different output:
#Root_id=null
id first_name reports_to_id Manager_id Manager_first_name LevelOf
------ ---------- ------------- ---------- ------------------ -----------
1 Jerome NULL NULL NULL 1
2 Joe 1 1 Jerome 2
9 Bill 1 1 Jerome 2
10 Sam 9 9 Bill 3
3 Paul 2 2 Joe 3
6 David 2 2 Joe 3
7 Ian 6 6 David 4
8 Helen 6 6 David 4
4 Jack 3 3 Paul 4
5 Daniel 3 3 Paul 4
(10 row(s) affected)
There is an option to avoid recurrency in tree browsing queries. You can add 'Path' column to your categories tree. It should contain each element ancestors IDs delimited with some non-numeric character (like slash).
For example your "ID=4" category's path would look like that: "/1/2/3/"
Now when you join your ads table to categories you need to do following:
select * from ads_table
inner join cat_table on cat_table.ID = ads_table.category_ID
where cat_table.Path like '/1/%'
And that's your query.
You can read more on this topic on my blog post
Are you familiar with Common Table Expressions in SQL Server? One of the many uses a CTE has is to do recursive queries.
The following is one of the best articles I've found on the subject:
https://web.archive.org/web/20210927200924/http://www.4guysfromrolla.com/webtech/071906-1.shtml

simplest way to do recursive t-sql for multiple selects

I am developing a Bill Of Materials cost calculator program and I am struggling to fathom a simple solution to some recursive selects I want.
I am using SQL Server 2005 for this part of the application.
Say I have Product A, which contains assembly B, and Part C. Assembly B will contain parts D and E, but, here is where I struggle, D and or E may contain X number of other assemblies.
I can do something along the lines of;
SELECT * FROM TBLBOM WHERE Parent = A
UNION
SELECT * FROM TBLBOM WHERE Parent = B
UNION
SELECT * FROM TBLBOM WHERE Parent = C
To produce something along the lines of;
PARENT COMP COST
A X £1
B D £0.5
B E £0.5
....
C Y £1
But lets say Component D is made up of Component F & G, how would I accommodate this in a t-sql statement.
In a nutshell, I need to expand out the full component list of all assemblies that are associated to a parent product regardless of whether they are in a sub assembly or a sub assembly of a sub assembly etc...
Ideally I would like to avoid a cursor at all costs :)
Any help / guidance would be appreciated.
Thank you.
EDIT;
As requested, here is the table structure and expected output. The parent is the DRAWINGNO and the child node is the PART (which could also be a parent in itself);
BOMID DRAWINGNO ITEM PART COST
1303 HGR05180 1 HGR05370 1
1304 HGR05180 2 HGF65050 4
1305 HGR05180 3 HGF50340 1
1312 HGR05370 1 HPN05075 1
1313 HGR05370 2 HPN05085 2
1314 HGR05370 3 HPN05080 1
1848 EXP-18G 1 HGR05180 1
1849 EXP-18G 2 HGR05210 3
1850 EXP-18G 3 HGR05230 1
1851 EXP-18G 4 HGR05140 1
1852 EXP-18G 5 HGR05150 2
1853 EXP-18G 6 HGR05050 1
1854 EXP-18G 7 ESC05350 1
1855 EXP-18G 8 ESC05330 3
1856 EXP-18G 9 HGR05360 1
1857 EXP-18G 10 HGR05370 2
1858 EXP-18G 11 ESC05640 1
If i understand (and without table structure) you can try something like this
DECLARE #Table TABLE(
Component VARCHAR(50),
Parent VARCHAR(50),
Cost FLOAT
)
INSERT INTO #Table SELECT 'B', 'A', 1
INSERT INTO #Table SELECT 'C', 'B', 2
INSERT INTO #Table SELECT 'C', 'B', 3
INSERT INTO #Table SELECT 'D', 'C', 4
DECLARE #Product VARCHAR(50)
SET #Product = 'A'
;WITH Selects AS (
SELECT *
FROM #Table
WHERE Parent = #Product
UNION ALL
SELECT t.*
FROM #Table t INNER JOIN
Selects s ON t.Parent = s.Component
)
SELECt *
FROm Selects
You want to be using recursive common table expression (CTEs). Books Online has a lot of information on how to use these; in the index, look up CTEs and pick the "Recursive Queries Using Common Table Expressions" entry. (I've had problems before linking to BOL online, or I'd try to link it here.)
Also, if you post your table structure, you should get half a dozen examples within five minutes. Better yet, try and search SO for prior examples.