Postgresql Skip Row if value is equal to last row - sql

in Postgres 9.1 is it possible to skip row(s) if the value of NAME is equal to the one before f.e. following table
ID | NAME | AGE | SEX | CLASS
---------------------------------
1 Paul 17 M 2b
2 Paul 16 M 2b
3 Paul 18 F 2b
4 Lexi 18 M 2b
5 Sarah 16 F 2b
6 Sarah 17 F 2b
The result should be:
1 Paul 17 M 2b
4 Lexi 18 M 2b
5 Sarah 16 F 2b
Thanks for your help,
t book

select *
from (
select id,
name,
age,
sex,
class,
lag(name) over (order by id) as prev_name
from the_table
) as t
where name <> prev_name;
alternatively
select *
from (
select id,
name,
age,
sex,
class,
row_number() over (partition by name order by id) as rn
from the_table
) as t
where rn = 1;
Another option would be to use Postgres' distinct on operator:
select distinct on (name)
id,
name,
age,
sex,
class
from the_table
order by name,id
but that will return the result ordered by name (which is limitation of the distinct on operator). If you don't want that you'll need to wrap this again:
select *
from (
select distinct on (name)
id,
name,
age,
sex,
class
from the_table
order by name,id
) t
order by id;

SELECT ID , NAME , AGE , SEX , CLASS
FROM thetable t
WHERE NOT EXISTS (
SELECT * FROM thetable nx
WHERE nx.NAME = t.NAME
-- AND nx.ID < t.ID -- ANY one before it
AND nx.ID = t.ID-1 -- THE one before it
);

Related

Select the non repeating/Distinct value in SQL

I'm trying to select the record based on the distinct id. When i go for 'DISTINCT' it picks the duplicate record and truncates the repeating record and gives me the one left out.
How can i SQL to pick to just that record which isn't repeated ?
INPUT
id
name
age
location
1
a
22
usa
1
a
23
usa
2
b
44
uk
3
e
33
eu
3
f
55
eu
8
k
49
usa
OUTPUT
id
name
age
location
2
b
44
uk
8
k
49
usa
ok , here is how you can do it :
select * from (
select * , count(*) over (partition by id) cn
from tablename
) t
where cn = 1
Try this:
SELECT *
FROM [Input]
WHERE ID IN (
SELECT ID FROM [Input] GROUP BY ID HAVING COUNT(ID) = 1
)
This should achieve the output you're after:
SELECT *
FROM yourtable
WHERE id IN (
SELECT id
FROM yourtable
GROUP BY id
HAVING COUNT(*) = 1)
You can use SQL Common Transaction Expression (CTE) AS FOLLOWS
declare #mytable as table(id int ,name nvarchar(100),age int,location nvarchar(50))
insert into #mytable values
(1,'a',22,'usa'),(1,'a',23,'usa'),(2,'b',44,'uk'),(3,'e',33,'eu'),(3,'f',55,'Tunisia'),('8','k',49,'Palestine')
with
cte1 as(select * from #mytable),
cte2 as (select id, count(1) N from #mytable group by id),
cte3 as (select TA.id,TA.name,TA.age,TA.location from cte1 TA inner join cte2 TB on TA.id=TB.id where TB.N=1)
select * from cte3

SQL statement for average in specific row values group columns

I've my table like the format below, I want to write sql statement for display column average for test1 and test2, column for main and column for position.
I've tried this sql:
select name, average as final,AVG(case when exam in('test1','test2') ) as testa
from table
where exam='main'
order by main final
group by name;
But It Does not working....help please
I want results to appear like this:
You could do something like the following.
Select t1.name, average12, main, rownum from (
select t1.name as name, t1.avg(average) as average12, t2.mainexam as main
from <table> t1 left join <table> t2
on t1.id = t2.id
where t2.exam = 'Main'
group by t1.name, t2.mainexam
) order by main desc;
Try this query for SQL Server:
SELECT
e.name AS 'Student Name'
, CAST( AVG( CASE WHEN e.Exam IN ( 'Test1', 'Test2' ) THEN e.average END ) AS FLOAT ) AS 'Test Average'
, e2.average AS 'Main Exam'
, ROW_NUMBER() OVER ( ORDER BY e2.average DESC ) AS 'Position'
FROM
exams AS e
INNER JOIN exams AS e2
ON e2.name = e.name
AND e2.exam = 'Main'
GROUP BY
e.name
, e2.average
ORDER BY
e2.average DESC
Output is:
+--------------+--------------+-----------+----------+
| Student Name | Test Average | Main Exam | Position |
+--------------+--------------+-----------+----------+
| Name1 | 60.5 | 89 | 1 |
| Name2 | 49.5 | 63 | 2 |
| Name3 | 73 | 38 | 3 |
+--------------+--------------+-----------+----------+
This simple query averages Test1 and Test2. Depending on your dbms, you might have trouble with reserved words like name.
select name, cast(avg(average) as integer)
from test
where exam = 'Test1'
or exam = 'Test2'
group by name;
This simple query gives you the values for "main".
select name, average as main
from test
where exam = 'Main'
Put them together in a common table expression, and join on "name".
with test_avg as (
select name, cast(avg(average) as integer)
from test
where exam = 'Test1'
or exam = 'Test2'
group by name
), main_avg as (
select name, average as main
from test
where exam = 'Main'
)
select t1.name, t1.avg, t2.main
from test_avg t1
inner join main_avg t2
on t1.name = t2.name;
name avg main
--
Name1 61 89
Name2 50 63
Name3 73 38
I don't know what you mean by position. If you're trying to rank the rows from highest average to lowest average, you might do it like this.
with test_avg as (
select name, cast(avg(average) as integer)
from test
where exam = 'Test1'
or exam = 'Test2'
group by name
), main_avg as (
select name, average as main
from test
where exam = 'Main'
)
select t1.name, t1.avg, t2.main, row_number() over (order by avg desc) as position
from test_avg t1
inner join main_avg t2
on t1.name = t2.name
order by position;
name avg main position
--
Name3 73 38 1
Name1 61 89 2
Name2 50 63 3
You can play with the max here.... Its very simple
select a.*,row_number() over (order by main desc) as position from
(
select name,
AVG(case when exam in ('Test1','Test2') then Average else null end ) as testa,
max(case when exam in ('Main') then Average else null end) as main
from abc
group by name
) a
order by name;
Output :
name testa main position1
Name1 60.50 89 1
Name2 49.50 63 2
Name3 73.00 38 3

Procedure to copy data from a table to another table in SQL Server

I have a table A, with 4 columns:
first_name, invoice, value, date.
And a table B (first_name, max_invoice_name, max_invoice_value, last_date)
I want to create a procedure in order to move data from A, to B, but:
first_name should be one time in B,
max_invoice_name is the name of the max invoice value
max_invoice_value is the max value
last_date is the latest date from invoices from the same first_name.
For example:
TABLE A:
Smith | Invoice1 | 100 | 23.06.2016
John | Invoice13 | 23 | 18.07.2016
Smith | Invoice3 | 200 | 01.01.2015
Table B should be:
Smith |Invoice3 | 200 | 23.06.2016
John |Invoice13| 23 | 18.07.2016
Something like this should work:
select *, (select max(date) from #Table1 T1 where T1.first_name = X.first_name)
from (
select
*,
row_number() over (partition by first_name order by invoice_Value desc) as RN
from
#Table1
) X
where RN = 1
Row number takes care of selecting the row with biggest value, and the max get's the date. You'll need to list the columns in correct place instead of *
You will need to create 2 scalar functions getMaxNameForMaxValue AND getLastDateByFirstName to get the values you want.
INSERT INTO TableB (first_name, max_invoice_name, max_invoice_value, last_date) (SELECT DISTINCT first_name, getMaxNameForMaxValue(MAX(max_value)) AS 'max_invoice_name', MAX(max_invoice_value) AS 'max_invoice_value', getLastDateByFirstName(first_name) AS 'lastDate' FROM Table A)
You can use something like this:
--INSERT INTO TableB
SELECT first_name,
invoice_name,
invoice_value,
last_date
FROM (
SELECT a.first_name,
a.invoice_name,
a.invoice_value,
COALESCE(p.last_date,a.last_date) as last_date,
ROW_NUMBER() OVER (PARTITION BY a.first_name ORDER BY a.last_date) as rn
FROM TableA a
OUTER APPLY (SELECT TOP 1 * FROM TableA WHERE first_name = a.first_name and last_date > a.last_date) as p
) as res
WHERE rn = 1
As output:
first_name invoice_name invoice_value last_date
John Invoice13 23 2016-07-18
Smith Invoice3 200 2016-06-23
Try this
Insert into TableB(first_name, max_invoice_name, max_invoice_value, last_date)
select t1.first_name,t1.invoice,t1,value,t2.date from TableA as t1 inner join
(
select first_name, max(replace(invoice,'invoice','')) as invoice, max(date) as date
from TableA group by first_name
) as t2 on t1.first_name=t2.first_name and t1.invoice=t2.invoice

How to find max value and its associated field values of each type in SQL?

class sid sname mark
1A 1 Pete 330
1A 2 Pet 150
1B 3 Pe 100
1B 4 Pe 20
1C 5 Peter 30
how to select the student information from each class who has the highest mark?
I can only do this
SELECT MAX(mark), class FROM student GROUP BY class
Please try:
select * from(
select
*,
row_number() over (partition by class order by mark desc) RNum
From YourTable a
)x where RNum=1
select t1.*
from your_table t1
inner join
(
select class, max(mark) as maxmark
from your_table
group by class
) t2 on t1.class = t2.class and t1.mark = t2.maxmark

Select row if column value not same as previous rows

I have a table like below
No. FName Age Tag
1 abc 22 c
2 xyz 60 c
3 pqr 62 i
4 abc 22 i
5 abc 32 i
I want the result to be returned as
No. FName Age Tag
1 abc 22 c
2 xyz 60 c
3 pqr 62 i
5 abc 32 i
Requirement is that if a column Name AND column Age value is same for rows with tag=c and tag=i , than the tag=i row should not be selected . Row no. 1 and Row no. 4 have same values (Name,age)=(abc,22) but row 4 has tag = i.So row no 4 above (abc,22,i) has to be left out. How can I do this ?
WITH records
AS
(
SELECT No, FName, Age, Tag,
ROW_NUMBER() OVER (PARTITION BY FName, Age
ORDER BY Tag ASC) rn
FROM tableName
WHERE TAG IN ('c','i')
)
SELECT No, FName, Age, Tag
FROM records
WHERE rn = 1
SQLFiddle Demo
if you have other special values on TAG,
WITH records
AS
(
SELECT No, FName, Age, Tag,
ROW_NUMBER() OVER (PARTITION BY FName, Age
ORDER BY CASE WHEN TAG = 'c' THEN 0 ELSE 1 END ASC) rn
FROM tableName
WHERE TAG IN ('c','i')
)
SELECT No, FName, Age, Tag
FROM records
WHERE rn = 1
ORDER BY No
SQLFiddle Demo
UPDATE 1
SELECT Fname, Age, MIN(tag) Tag
FROM TableName
WHERE TAG IN ('c','i')
GROUP BY Fname, Age
SQLFiddle Demo
I think you can simply ignore all the rows with tag = 'i' if already exists row with same age, name and tag = 'c' :
select * from TableName t1
where not exists
(select 1
from TableName
where FNAme = t1.FNAme and Age = t1.Age and t1.Tag = 'i' and tag = 'c')