Postgresql: two columns the same but third column different, display rows - postgresql

I have 4 columns id, firstname, last name, reference. Most times the reference is the same but I need to display each row that has the same names, but a different reference in postgresql and order them by the id number. ONLY for those rows which someone has more than one reference. I've tried a bunch of things but just made a mess. Please assist.
For example:-
20 / John / Smith / 675
21 / John / Smith / 675
22 / John / Smith / 676
22 / Joe / Bloggs/ 651
24 / Joe / Bloggs/ 651
25 / John / Smith / 674
Should return all the John Smith rows, because one or more of his references is dissimilar
20 / John / Smith / 675
21 / John / Smith / 675
22 / John / Smith / 676
25 / John / Smith / 674
I can use this to get a count, but I want to display the full rows
select firstname, lastname, count (distinct id)
from transfer
group by firstname, lastname
having count(distinct id) >= 2

You don't need to count, you only have to check if (at least) one record with a different value for reference for the same person exists:
SELECT *
FROM transfer t
WHERE EXISTS (
SELECT * FROM transfer x
WHERE x.firstname = t.firstname -- same name
AND x.lastname = t.lastname
AND x. reference <> t.reference -- different reference
);

This is a possible answer:
select id, firstname, lastname, reference
from transfer t1
where 1 < (select count(distinct reference)
from transfer t2
where t1.firstname = t2.firstname and t1.lastname = t2.lastname)

Related

Getting percentage change between selected data within a column in PostgreSQL

I am using PostgreSQL and I am trying to calculate the percentage change for two values in the same column and group them by the name column and I am having trouble.
Suppose I have the following table:
name
day
score
Allen
1
87
Allen
2
89
Allen
3
95
Bob
1
64
Bob
2
68
Bob
3
75
Carl
1
71
Carl
2
77
Carl
3
80
I want the result to be the name and the percentage change for each person between day 3 and day 1. So Allen would be 9.2 because from 87 to 95 is a 9.2 percent increase.
I want the result to be:
name
percent_change
Allen
9.2
Bob
17.2
Carl
12.7
Thanks for your help.
Try this...
with dummy_table as (
select
name,
day,
score as first_day_score,
lag(score, 2) over (partition by name order by day desc) as last_day_score
from YOUR_TABLE_NAME
)
select
name,
(last_day_score - first_day_score) / first_day_score::decimal as percentage_change
from dummy_table where last_day_score is not null
Just replace YOUR_TABLE_NAME. There are likely more performant and fancier solutions, but this works.
You can try with lag function, something like this:
select name, day, score, 100*(score - lag(score, 1) over (partition by name order by day))/(lag(score, 1) over (partition by name order by day)) as growth_percentage

Recursive Manager/Associate query in T/SQL

looking at this example link
I want to be able to walk through an HR table that I populate with who the manager is that is looking up themself and their reports (which shows them what level they are also)
Data:
AssociateID AssociateName ManagerID AssociateTitle
=========== ============= ========= ==============
1 Jack Frost Chief Executive Officer
2 Cindy Smith 1 President of Sales
3 William Howard 1 President of Technology
4 Ben Samson 2 Director of Tradeshow
5 Sarah Jones 2 Director of Documentation
6 Laurie Ralph 4 Manager
7 Tina Nelson 5 Manager
8 Joe May 6 Coordinator
9 Peter Gill 7 Coordinator
10 Kelly Kraft 2 Director of Facilities
11 Heidi Trump 8 Administrative Assistant
If CEO signs in he gets the following data:
Jack Frost
Cindy Smith
Ben Samson
Laurie Ralph
Joe May
Heidi Trump
William Howard
But if Cindy Smith signs in her list would be:
Cindy Smith
Ben Samson
Laurie Ralph
Joe May
Heidi Trump
The indenting is for visual. I was also wondering if there could be a column that indicates what level the people are below. Level 1; Level 2; etc
And if a person who signs in (example: Heidi Trump) her list is only her name.
Is this possible with TSQL?
I have it and want to share:
DECLARE #Associate float
Set #Associate = 1
;
WITH DirectReports(ManagerID, EmployeeID, Associate, Title, EmployeeLevel) AS
(
SELECT ManagerID, AssociateID, LegalLastName + ', ' + LegalFirstName, Title, 1 AS EmployeeLevel
FROM AssociateMaster
WHERE
ManagerID = #Associate
UNION ALL
SELECT e.ManagerID, e.AssociateID, e.LegalLastName + ', ' + e.LegalFirstName, e.Title, EmployeeLevel + 1
FROM dbo.AssociateMaster AS e
INNER JOIN DirectReports AS d
ON e.ManagerID = d.EmployeeID
)
SELECT
DR.ManagerID, MGR.LegalLastName + ', ' + MGR.LegalFirstName AS AssociateName, DR.EmployeeID, DR.Associate, DR.Title, DR.EmployeeLevel
FROM DirectReports DR
INNER JOIN AssociateMaster as Mgr ON DR.ManagerID = Mgr.AssociateID
UNION
SELECT ' ', ' ', AssociateID, LegalLastName + ', ' + LegalFirstName, Title, 0
FROM AssociateMaster
WHERE
AssociateID = #Associate
ORDER BY
DR.EmployeeLevel Asc,
MGR.LegalLastName + ', ' + MGR.LegalFirstName ASC,
DR.Associate Asc
OPTION (MAXRECURSION 0);

GROUP BY name and ORDER BY point & time MYSQLi

I'm new to this forum and I hope to find my solution about my problem.
I have this table :
name time points car date
Daniel | 55s | 210 | red |20/01/2018
Daniel | 45s | 250 | green |21/01/2018
Julie | 54s | 220 | red |19/01/2018
Julie | 33s | 150 | yellow|22/01/2018
and I wish to sort it like this
name time points car date
Daniel | 45s | 250 |green |21/01/2018
Julie | 54s | 220 |red |19/01/2018
first sorting by points, than sorting by time and group by name (optional the count)
I use this
SELECT NAME, MAX(POINTS) POINTS, MAX(TIME) TIME, MAX(CAR) CAR, MAX(DATE) DATE
FROM ( SELECT A.* FROM test A LEFT OUTER JOIN test B ON A.NAME=B.NAME AND
A.POINTS<B.POINTS AND A.TIME>B.TIME WHERE B.NAME IS NULL ) as sub GROUP BY NAME
and I get this :
name POINTS TIME CAR DATE
Daniel 250 45 green 2018-01-21
Julie 220 54 yellow 2018-01-22
Julie should have car=red & 2018-01-19
For Daniel it looks good
how can I get thise values (car & date) ?
thanks
Nico
You could give this a shot. It contains the table against itself and gets only records with the highest points and lowest time.
SELECT NAME, MAX(POINTS) POINTS, MAX(TIME) TIME
FROM
(
SELECT A.* FROM test A
LEFT OUTER JOIN test B ON A.NAME=B.NAME AND A.POINTS<B.POINTS AND A.TIME>B.TIME
WHERE B.NAME IS NULL
) GROUP BY NAME
For additional, try this:
SELECT * FROM
(
SELECT OUTERTEST.*,
#row_num := IF(#prev_value=OUTERTEST.name,#row_num+1,1) AS RowNumber,
#prev_value := OUTERTEST.name
FROM (SELECT * FROM TEST ORDER BY NAME, TEST.POINTS DESC, TEST.TIME ASC) OUTERTEST, (SELECT #row_num := 1, #prev_value := '') x
) A
WHERE A.ROWNUMBER=1
I did more test with this table
id name time points
1 Daniel 55 1140
2 Judie 54 1144
3 Judie 33 1028
4 Daniel 45 1180
5 Judie 53 1148
I apply this request
SELECT NAME, MAX(POINTS) POINTS, sub.TIME FROM (SELECT * FROM Testpoint ORDER BY POINTS DESC, TIME ASC) AS sub
GROUP BY sub.name
I have the max point for each name but the time is not the right one
name POINTS time
Daniel 1180 55
Judie 1148 54
Judie should have 53 for time and not 54
What I did wrong?
thankyou
Nico

Return all records regardless if there is a match

In my Table 1, It may have AND have a null entry in the address column to corresponding record OR not have a matching entry in Table 2.
I want to present all the records in Table 1 but also present corresponding entries from Table 2. My RESULT is what I am trying to achieve.
Table 1
ID First Last
1 John Smith
2 Bob Long
3 Bill Davis
4 Sam Bird
5 Tom Fenton
6 Mary Willis
Table 2
RefID ID Address
1 1 123 Main
2 2 555 Center
3 3 626 Smith
4 4 412 Walnut
5 1
6 2 555 Center
7 3
8 4 412 Walnut
Result
Id First Last Address
1 John Smith 123 Main
2 Bob Long 555 Center
3 Bill Davis 626 Smith
4 Sam Bird 412 Walnut
5 Tom Fenton
6 Mary Willis
You need an outer join for this:
SELECT * FROM Table1 t1 LEFT OUTER JOIN Table2 t2 ON t1.ID = t2.RefID
How do you join those two tables? If table 2 have more than 1 matched address, how do you want display them? Please clarify in your question.
Here is a query based on my assumptions.
SELECT
ID, First, Last,
Address = (SELECT MAX(Address) FROM Table2 t2 WHERE t1.ID = t2.ID)
FROM Table1 t1

TSQL, remove negative values in a group

I have a table with following data:
Id Name Value
1 John 100
2 John -500
3 John 500
4 Smith 10
5 Smith 20
6 Smith -20
7 Stuart -10
8 Wills 25
I am looking for an efficient TSQL query which can remove John -500 and Smith -20 (i.e. records with negative value if they have a similar positive value in the same group [group by names]).
I think this is what you need. (SQL DEMO)
delete y
from mytable y join (
select id,name, value
from mytable x
where value > 0) z on y.name = z.name and y.value = -1 * z.value
select * from mytable
--SELECT RESULTS AFTER DELETING
ID NAME VALUE
1 John 100
3 John 500
4 Smith 10
5 Smith 20
7 Stuart -10
8 Wills 25
delete a
from mytable a
join mytable b
on b.name = a.name
and a.value < 0
and b.value = -1 * z.value
Almost the same as Kaf +1