Sort out some entries from result of a query - sql-server-2008

I can not explain what exactly I want in words that's why I prefer to show a small example.
I am using SQL Server, and have this table:
col1 | col2 | col3
--------+----------+------------
john | conner | 01.01.1980
sarah | conner | 03.04.1978
john | conner | 01.01.1977
I have a table with 3 entries (for example) with 2 of them are almost the same with a different in the col3.
How can I delete the entry with the smallst col3 (which is 01.01.1980) and keep the one with 01.01.1977, so I get this table as final result
col1 | col2 | col3
--------+----------+------------
sarah | conner | 03.04.1978
john | conner | 01.01.1977
So simply is my question : how can I compare entries in the same table and take the entries that are almost the same but with a different in the col3 which is the Age and I want to take the older John Conner ...
Thanks in advance

CREATE TABLE TabStack
(
col1 varchar(10),
col2 varchar(10),
col3 date
)
insert into TabStack
values(
'john','conner','01.01.1980'),
('sarah','conner','03.04.1978'),
('john','conner','01.01.1977')
with cte
as
(
select col1, col2, col3, ROW_NUMBER() over(partition by col1,col2 order by col3) rn
from TabStack
)
delete from cte
where rn>1
--select * from TabStack

A simple GROUP BY query should work:
SELECT
col1,
col2,
MIN(CONVERT(datetime, col3, 104)) AS col3
-- or just MIN(col3) AS col3
FROM yourTable
GROUP BY
col1,
col2
I'm not sure if you stored your dates as text or as actual dates. If the former, then you can use CONVERT() to bring them to datetime before using MIN(). If the latter, then just use MIN(col3) to get the earlier dates.
Output:
Demo here:
Rextester
Update:
Here is a general query which will return the entire max record per group, along with ties:
SELECT
t.col1, t.col2, t.col3, t.col4, ...
FROM
(
SELECT t.*,
DENSE_RANK() OVER (PARTITION BY col1, col2 ORDER BY col3) dr
FROM yourTable t
) t
WHERE t.dr = 1;
If you don't want ties, then replace DENSE_RANK with ROW_NUMBER.

Related

Averages by multiple columns separately

I have a collection table1 with the following columns:
id (INT)
col1 (VARCHAR)
col2 (VARCHAR)
value (INT)
I want to calculate the average separately by col1 and by col2 to have a response like this:
{
averageByCol1: {col1Value1: 23, col1Value2: 44},
averageByCol2: {col2Value1: 33, col2Value2: 91}
}
Tried to use multiple columns in GROUP BY, but this combines the columns:
SELECT
CONCAT(col1, col2, AVG(value))
FROM table1
GROUP BY col1, col2
Also tried with subquery but it gives me Subquery returns more than 1 row error:
SELECT
(SELECT
CONCAT(col1, AVG(value))
FROM table1
GROUP BY col1) AS col1Averages,
(SELECT
CONCAT(col2, AVG(value))
FROM table1
GROUP BY col2) AS col2Averages;
Using Mysql v5.5.
edit with sample data:
id col1 col2 value
1 v1 b1 34
2 v2 b1 65
3 v1 b1 87
4 v1 b2 78
5 v2 b2 78
6 v1 b2 12
Want average of value by v1, v2, b1, and b2 independently.
Use a UNION for each column you want to calculate an average for
SELECT col1 as col_key, avg(value) as average
FROM test
GROUP BY col1
UNION
SELECT col2, avg(value)
FROM test
GROUP BY col2
this will work:
select avg(value),col1 from Table1 group by col1
union all
select avg(value),col2 from Table1 group by col2
sql fiddle:http://sqlfiddle.com/#!9/c1f111/5/0
If you want 2 queries for separate results:
SELECT col1, AVG(value) AS average1
from table1
GROUP BY col1
ORDER BY col1
and
SELECT col2, AVG(value) AS average2
from table1
GROUP BY col2
ORDER BY col2

MySQL Counting the number of occurrences of a value from a column in another column and storing in new column

How do I structure my query so I can count how many occurrences of a value in column 1 appears in column 2 and then store that result in a new column in the same table? (If a value is duplicated in the first column I still want to store the same value in the new column) For example if I had a table like this:
COL1 COL2
1 2
1 4
2 1
3 1
4 1
4 2
The resulting table will look like this:
COL1 COL2 COL3
1 2 3
1 4 3
2 1 2
3 1 0
4 1 1
4 2 1
Any help is appreciated I am new to sql! Thanks in advance!
Select
col1,
col2,
COALESCE(col3,0) as col3
FROM
mytable
LEFT JOIN
( Select count(*) as col3, col2
from mytable
GROUP BY col2) as temp ON temp.col2 = mytable.col1
And if you want the update (thanks Thorsten Kettner ) :
UPDATE mytable
LEFT JOIN ( Select count(*) as col3, col2
from mytable
GROUP BY col2) as temp ON temp.col2 = mytable.col1
SET mytable.col3 = COALESCE(temp.col3,0)
You can easily count on-the-fly. Don't store this redundantly. This would only cause problems later.
select
col1,
col2,
(
select count(*)
from mytable match
where match.col2 = mytable.col1
) as col3
from mytable;
If you think you must do it; here is the according UPDATE statement:
update mytable
set col3 =
(
select count(*)
from mytable match
where match.col2 = mytable.col1
);
To do that, you can try :
SELECT COL1, COL2, (SELECT COUNT(COL1) FROM `tablename` AS t2
WHERE t2.COL1 = t1.COL1) AS COL3 FROM `tablename` AS t1
Enjoy :)

How to get SUM of certain column without losing all rows?

CREATE TABLE tmp ( col1 int, col2 int );
INSERT INTO tmp VALUES (1,3), (2,5), (3,7);
SELECT col1, col2, SUM(col2) AS Total FROM tmp; -- ???
The SELECT statement leaves me with this data set:
col1 col2 Total
1 3 15
Is there a way to allow all the rows to appear without introducing a subquery, so that the result is this:
col1 col2 Total
1 3 15
2 5 15
3 7 15
You can use a cross join to avoid a subquery:
SELECT t1.col1, t1.col2, sum(t2.col2) sum_col2
from tmp t1
cross join tmp t2
group by 1, 2
See SQL fiddle
Note that this only works if combinations of col1 and col2 are unique.

SQL: Get at least the Third Largest Value from a set of columns of a row

I need a single SQL query to get the second largest value from a set of columns of a row.
For example, if these are my table's rows:
id | col1 | col2 | col3 | col4 | coln |
1 | 5 | 7 | 9 | 3 | 10 |
2 | 13 | 14 | 2 | 54 | 11 |
For rowid 1 - I need the value 9,
rowid 2 - I need the value 14
I'm afraid that, without common table expressions and/or window functions and without resorting to writing a procedure, this gets horribly verbose in MySQL
SELECT t.id, t.val second_largest
-- unpivot your columns into a table
FROM (
SELECT id, col1 val FROM my_table UNION ALL
SELECT id, col2 FROM my_table UNION ALL
SELECT id, col3 FROM my_table UNION ALL
SELECT id, col4 FROM my_table UNION ALL
SELECT id, coln FROM my_table
) t
-- retain only those records, where there exists exactly one record with a
-- column value greater than any other column value with the same id
WHERE 1 = (
SELECT COUNT(*)
-- Here, use unions to be sure that every value appears exactly once
FROM (
SELECT id, col1 val FROM my_table UNION
SELECT id, col2 FROM my_table UNION
SELECT id, col3 FROM my_table UNION
SELECT id, col4 FROM my_table UNION
SELECT id, coln FROM my_table
) u
WHERE t.id = u.id
AND t.val < u.val
)
Here's the SQLFiddle to check it (thanks to bluefeet for the heads-up with the schema!). The above solution will find the second largest column value in every row, even if the largest column value appears more than once.
You could do this by unpivoting the data and then applying a row number to each record in the id group. The unpivot takes the data from the column layout and places it into rows so it is easier to determine the second highest value:
select id, col, value
from
(
-- assign a group row number to each record
select *,
#row:=(case when #prev=id and #prevvalue<>value then #row else 0 end) + 1 as rownum,
#prevvalue:=value,
#prev:=id pid
from
(
-- unpivot the multi columns into row values
select id, 'col1' col, col1 value
from yourtable
union all
select id, 'col2' col, col2 value
from yourtable
union all
select id, 'col3' col, col3 value
from yourtable
union all
select id, 'col4' col, col4 value
from yourtable
union all
select id, 'coln' col, coln value
from yourtable
) src
order by id, value desc
) src
-- apply filter looking for the rownumber = 2 which is the second highest based on order
where rownum = 2
See SQL Fiddle with Demo
The result will show:
| ID | COL | VALUE |
---------------------
| 1 | col3 | 9 |
| 2 | col2 | 14 |
SELECT MAX(col) FROM table WHERE col NOT IN (SELECT MAX(col) FROM table);
This gives you the second largest integer value in a specific column.
EDIT:
then just swap the rows with the columns before doing that. But if the columns are dynamic, it could be quite tricky.
The best/easiest way would be to use a client side language and not SQL directly for this specific operation. If not possible, check this: Transpose rows and columns without aggregate

MySQL query: find the minimum non-null value across all the columns in that record

I have a table with the following structure
id(int) | col1(int) | col2(int) | col3(int)
------------------------------------------
1 | NULL | 10 | 20
2 | 5 | NULL | 30
3 | 8 | NULL | NULL
Given an 'id' value I need to find the minimum non-null value across all the columns in that record.
Like for example, for id=1, the value is 10. For id =2 the value is 5 and so on.
How do I do this in MySQL?
LEAST() would be best if it was NULL safe, but since it isn't, this is the cleanest solution i can think of:
SELECT MIN(cols)
FROM (SELECT col1
FROM table
WHERE id = 1
UNION
SELECT col2
FROM table
WHERE id = 1
UNION col3
FROM table
WHERE id = 1) AS dt
if you need it in one line, this is the best i can think of:
SELECT LEAST(COALESCE(col1, col2, col3)
, COALESCE(col2, col3, col1)
, COALESCE(col3, col1, col2))
FROM table
WHERE id = 1
Give this a shot:
SELECT id, LEAST(
ifnull(ifnull(col1, col2), col3),
ifnull(ifnull(col2, col1), col3),
ifnull(ifnull(col3, col1), col2)) FROM table;
SELECT LEAST(
COALESCE(col1, col2, col3),
COALESCE(col2, col1, col3),
COALESCE(col3, col1, col2)
)
FROM mytable
Alternatively, use session variables:
SELECT LEAST(
#r := COALESCE(col1, col2, col3),
#r := COALESCE(#r, col2),
#r := COALESCE(#r, col3)
)
FROM mytable
Been a while since I used them, but I think you can use MySQL's MIN and LEAST functions like so:
SELECT MIN(LEAST(col1,col2,col3)) FROM table WHERE id=1;