Collapse SQL rows - sql

Say I have this table:
id | name
-------------
1 | john
2 | steve
3 | steve
4 | john
5 | steve
I only want the rows that are unique compared to the previous row, these:
id | name
-------------
1 | john
2 | steve
4 | john
5 | steve
I can partly achieve this by using this query:
SELECT *, (
SELECT `name` FROM demotable WHERE id=t.id-1
) AS prevName FROM demotable AS t GROUP BY prevName ORDER BY id ASC
But when I am using a query with multiple UNIONs and stuff, this gets way to complicated. Is there an easy way to do this (like GROUP BY, but than more specific)?

This should work, but I don't know if it's simpler :
select demotable.*
from demotable
left join demotable as prev on prev.id = demotable.id - 1
where demotable.name != prev.name

Related

Count how many times a value appears in tables SQL

Here's the situation:
So, in my database, a person is "responsible" for job X and "linked" to job Y. What I want is a query that returns: name of person, his ID and he number of jobs it's linked/responsible. So far I got this:
select id_job, count(id_job) number_jobs
from
(
select responsible.id
from responsible
union all
select linked.id
from linked
GROUP BY id
) id_job
GROUP BY id_job
And it returns a table with id in the first column and number of occurrences in the second. Now, what I can't do is associate the name of person to the table. When i put that in the "select" from beginning it gives me all the possible combinations... How can I solve this? Thanks in advance!
Example data and desirable output:
| Person |
id | name
1 | John
2 | Francis
3 | Chuck
4 | Anthony
| Responsible |
process_no | id
100 | 2
200 | 2
300 | 1
400 | 4
| Linked |
process_no | id
101 | 4
201 | 1
301 | 1
401 | 2
OUTPUT:
| OUTPUT |
id | name | number_jobs
1 | John | 3
2 | Francis | 3
3 | Chuck | 0
4 | Anthony | 2
Try this way
select prs.id, prs.name, count(*) from Person prs
join(select process_no, id
from Responsible res
Union all
select process_no, id
from Linked lin ) a on a.id=prs.id
group by prs.id, prs.name
I would recommend aggregating each of the tables by the person and then joining the results back to the person table:
select p.*, coalesce(r.cnt, 0) + coalesce(l.cnt, 0) as numjobs
from person p left join
(select id, count(*) as cnt
from responsible
group by id
) r
on r.id = p.id left join
(select id, count(*) as cnt
from linked
group by id
) l
on l.id = p.id;
select id, name, count(process_no) FROM (
select pr.id, pr.name, res.process_no from Person pr
LEFT JOIN Responsible res on pr.id = res.id
UNION
select pr.id, pr.name, lin.process_no from Person pr
LEFT JOIN Linked lin on pr.id = lin.id) src
group by id, name
order by id
Query ain't tested, give it a shot, but this is the way you want to go

Sum Decode statement SQL

I am trying to sum a few Decode statements and column names, but am having difficulties.
currently it is showing as
rank | name | points
----------------------
0 | john | 0
0 | john | 40
1 | john | 30
2 | tom | 22
0 | tom | 0
I expect to have this result:
rank | name | points
----------------------
1 | john | 70
2 | tom | 22
Query:
Select Rank, Name, Code, Points
From
(select
decode(Table.name, 'condition1', Table.value) As Points,
decode(Table.name, 'Condition2', Table.value) As Rank,
Employee.name as Name,
Employee.GA1 as Code
from Table
inner Join Employee
on Empolyee.positionseq = name.positionseq
where Table.name IN ('Condition1', 'Condition2')
);
Select MAX(Rank), Name, Code, SUM(Points)
From
(select
decode(Table.name, 'condition1', Table.value) As Points
decode(Table.name, 'Condition2', Table.value) As Rank
,Employee.name as Name
,Employee.GA1 as Code
from Table
inner Join Employee
on Employee.positionseq = name.positionseq
where Table.name IN( 'Condition1', 'Condition2'))
GROUP BY Employee.id;
I added the SUM, MAX (for rank) and GROUP BY statements. Also corrected some misspellings (Empolyee)
I may be understanding your question incorrectly, however, it seems like you are trying to do the following (omitting inner join for simplicity):
Select MAX(rank), name, SUM(points)
FROM UserRanks
GROUP BY name
Based on your data set above, you should get the following results:
rank name points
1 john 70
2 tom 22

Select multiple distinct rows from table SQL

I am attempting to select distinct (last updated) rows from a table in my database. I am trying to get the last updated row for each "Sub section". However I cannot find a way to achieve this.
The table looks like:
ID | Name |LastUpdated | Section | Sub |
1 | Name1 | 2013-04-07 16:38:18.837 | 1 | 1 |
2 | Name2 | 2013-04-07 15:38:18.837 | 1 | 2 |
3 | Name3 | 2013-04-07 12:38:18.837 | 1 | 1 |
4 | Name4 | 2013-04-07 13:38:18.837 | 1 | 3 |
5 | Name5 | 2013-04-07 17:38:18.837 | 1 | 3 |
What I am trying to get my SQL Statement to do is return rows:
1, 2, and 5.
They are distinct for the Sub, and the most recent.
I have tried:
SELECT DISTINCT Sub, LastUpdated, Name
FROM TABLE
WHERE LastUpdated = (SELECT MAX(LastUpdated) FROM TABLE WHERE Section = 1)
Which only returns the distinct row for the most recent updated Row. Which makes sense.
I have googled what I am trying, and checked relevant posts on here. However not managed to find one which really answers what I am trying.
You can use the row_number() window function to assign numbers for each partition of rows with the same value of Sub. Using order by LastUpdated desc, the row with row number one will be the latest row:
select *
from (
select row_number() over (
partition by Sub
order by LastUpdated desc) as rn
, *
from YourTable
) as SubQueryAlias
where rn = 1
Wouldn't it be enough to use group by?
SELECT DISTINCT MIN(Sub), MAX(LastUpdated), MIN(NAME) FROM TABLE GROUP BY Sub Where Section = 1

Data Matching with SQL and assigning Identity ID's

How to write a query that will match data and produce and identity for it.
For Example:
RecordID | Name
1 | John
2 | John
3 | Smith
4 | Smith
5 | Smith
6 | Carl
I want a query which will assign an identity after matching exactly on Name.
Expected Output:
RecordID | Name | ID
1 | John | 1X
2 | John | 1X
3 | Smith | 1Y
4 | Smith | 1Y
5 | Smith | 1Y
6 | Carl | 1Z
Note: The ID should be unique for every match. Also, it can be numbers or varchar.
Can somebody help me with this? The main thing is to assign the ID's.
Thanks.
How about this:
with temp as
(
select 1 as id,'John' as name
union
select 2,'John'
union
select 3,'Smith'
union
select 4,'Smith'
union
select 5,'Smith'
union
select 6,'Carl'
)
SELECT *, DENSE_RANK() OVER
(ORDER BY Name) as NewId
FROM TEMP
Order by id
The first part is for testing purposes only.
Please try:
SELECT *,
Rank() over (order by Name ASC)
FROM table
This structure seems to work:
CREATE TABLE #Table
(
Department VARCHAR(100),
Name VARCHAR(100)
);
INSERT INTO #Table VALUES
('Sales','michaeljackson'),
('Sales','michaeljackson'),
('Sales','jim'),
('Sales','jim'),
('Sales','jill'),
('Sales','jill'),
('Sales','jill'),
('Sales','j');
WITH Cte_Rank AS
(
SELECT [Name],
rw = ROW_NUMBER() OVER (ORDER BY [Name])
FROM #Table
GROUP BY [Name]
)
SELECT a.Department,
a.Name,
b.rw
FROM #Table a
INNER JOIN Cte_Rank b
ON a.Name = b.Name;

SQL Select with multiple IDs

I have an Address History table with three fields: EmpID, Address, AddrID.
Every time I add a new address, I also increment the Address ID (AddrID) by 1 for that particular employee.
EmpID | AddrID | Address
-------------------------------
1 | 1 | 1234 First Ave
1 | 2 | 2145 First Ave
1 | 3 | 1111 First Ave
2 | 1 | 1001 Second St
2 | 2 | 1002 Second St
2 | 3 | 1003 Second St
2 | 4 | 2222 Second St
3 | 1 | 3332 Third Lane
3 | 2 | 3333 Third Lane
4 | 1 | 4444 Fourth Way
How do I get the most recent address (highest Address ID) for each employee? Ideally, I should be able to return:
EmpID | AddrID | Address
-------------------------------
1 | 3 | 1111 First Ave
2 | 4 | 2222 Second St
3 | 2 | 3333 Third Lane
4 | 1 | 4444 Fourth Way
So far I have either returned too many results (ie, every Employee, every AddrID 1, and every Address associated with the two), or too few results (ie, every Employee with an AddrID 4 - just Employee 2).
I have tried using Distinct, Group By, Order By, Having, and Self-Joins to no avail.
What am I missing?
You can use a subquery that gets the MAX() addrid for each empid:
select t1.empid,
t1.addrid,
t1.address
from table1 t1
inner join
(
select max(addrid) addrid, empid
from table1
group by empid
) t2
on t1.empid = t2.empid
and t1.addrid = t2.addrid
See SQL Fiddle With Demo
The inner query will return the max addrid and the empid then you join your table to that result on those two values this will limit the records that get returned.
The following should work:
SELECT *
FROM (SELECT empid,
Max(addrid) AS AddrID
FROM t1
GROUP BY empid) a
JOIN t1 b
ON b.empid = a.empid
AND b.addrid = a.addrid
See it in action
I would probably organize the table differently, but you could do something with a sub-join
select * from address_history
join
(select EmpID, max(AddrID) max_address from Address_History group by empId) as latest_addresses
on address_history.empid = latest_addresses
and address_history.addrId = latest_addresses.max_address