I have data looking like this:
Is it possible to count using id as a column order (ASC) what is max value 0 occurrence in a ROW?
So expected result will be 0 was 3 times in a row; or if we count value 1 - 2 times in a row.
Well in php I could foreach all values and count occurences but i'm looking for solution to do that in database.
Kind regards
Mark
You can use some a gaps-and-island technique for this, using the difference between row numbers to build groups of consecutive records having the same value.
If ids are always incrementing by 1 (with no gaps):
select sl, max(no_rec)
from (
select t.*, count(*) over(partition by sl, id - rn) no_rec
from (
select t.*, row_number() over(partition by sl order by id) rn
from mytable t
) t
) t
group by sl
Otherwise, we can generate a fake autoincremented id with row_number():
select sl, max(no_rec)
from (
select t.*, count(*) over(partition by sl, rn1 - rn2) no_rec
from (
select
t.*,
row_number() over(order by id) rn
row_number() over(partition by sl order by id) rn
from mytable t
) t
) t
group by sl
Note: this uses window functions, which require MySQL 8.0. In earlier versions, such problem is much more cumbersome to solve.
Related
Lets say I have a table with the following rows/values:
I need a way to select the values in amount but only once if they're duplicated. So from this example I'd want to select A,B and C the amount once. The SQL result should look like this then:
Use LAG() function and compare previous amount with current row amount for name.
-- MySQL (v5.8)
SELECT t.name
, CASE WHEN t.amount = t.prev_val THEN '' ELSE amount END amount
FROM (SELECT *
, LAG(amount) OVER (PARTITION BY name ORDER BY name) prev_val
FROM test) t
Please check from url https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=8c1af9afcadf2849a85ad045df7ed580
You can handle situation like these with different function depending on what you need:
Case1 : If you have same values per name:
select distinct name, amount from [table name]
Case2 : You have duplicates with different values for each name and you want to pick the one with the highest value. Use min() if you need the minimum one to show up.
select name, max(amount) from [table name] group by 1
Case 3: The one you need with blanks for the rest of the duplications.
Row number will create rows based on values in amount and since the values are the same it will create it incrementally and you can then use IF to create a new column where rank_ > 1 then blanks. This will also cover the case where you would like to select just the minimum value and then have blanks for the rest of the name values
With ranking as (
select
*,
ROW_NUMBER() OVER(PARTITION BY NAME ORDER BY AMOUNT) AS RANK_
from [table]
)
SELECT
*,
IF(RANK_ > 1,"",AMOUNT) AS NEW_AMOUNT
FROM ranking
Case 4: You need to select maximum and put the other names as blank
You will just adjust the order by clause of ROW_NUMBER() to DESC. This will put the rank 1 to the highest amount per name and for the rest, the blank will be filled
With ranking as (
select
*,
ROW_NUMBER() OVER(PARTITION BY NAME ORDER BY AMOUNT DESC) AS RANK_
from [table]
)
SELECT
*,
IF(RANK_ > 1,"",AMOUNT) AS NEW_AMOUNT
FROM ranking
If you are using mysql 8 you can use row_number for this:
with x as (
select *, row_number() over(partition by name order by amount) rn
from t
)
select name, case when rn=1 then amount else '' end amount
from x
See example Fiddle
The other answers are missing a really important point: A SQL table returns an unordered set unless there is an explicit order by.
The data that you have provides has rows that are exact duplicates. For this reason, I think the best approach uses row_number() and an order by in the outer query:
select name, (case when seqnum = 1 then amount end) as amount
from (select t.*,
row_number() over (partition by name, amount) as seqnum
from t
) t
order by name, seqnum;
Note that MySQL does not require an order by argument for row_number().
More commonly, though, you would have some other column (say a date or id) that would be used for ordering. I should also emphasize that this type of formatting is often handled at the application layer and not in the database.
How can i identify the last Row of a distinct set of data in the field for an Alias Column (signaling somehow, with "1" for example).
For this example i need to know, when the ordered GROUP "CARS, COLORS, DRINKS, FRUITS" ends.
Check my intended result on this image:
My base query:
SELECT * FROM `MY_DB` ORDER BY `ITEM`, `GROUP` ASC
As a starter: rows of a SQL table are unordered. There is no inherent ordering of rows. For your question to make sense, you need a column that defines the ordering of the rows in each group - I assumed id.
Then: in MySQL 8.0, one option uses window functions:
select t.*,
(row_number() over(partition by grp order by id desc) = 1) as last_group_flag
from mytable t
In earlier versions, you could use a subquery:
select t.*,
(id = (select max(t1.id) from mytable t1 where t1.grp = t.grp)) as last_group_flag
from mytable t
Note: group is a language keyword, hence not a good choice for a column name. I used grp instead in the query.
You need ordering by item column and order by group column to find the last record per distinct group column.
Use row_number as follows:
select t.*,
Case when row_number() over(partition by group
order by item desc) = 1
then 1 else 0 end as last_group_flag
from your_table t
I have a table with columns weight,height,knee,date,id I wanted to find two rows in the same table- the first row is min(weight) and the other is max(weight). How do I also access the dates and ids for these two rows even though everything is in the same table.
Try searching for the min and max weight:
SELECT *
FROM yourTable
WHERE
weight = (SELECT MIN(weight) FROM yourTable) OR
weight = (SELECT MAX(weight) FROM yourTable);
Note that if there be more than one record tied for the min/max weight, then the above query could yield more than 2 records. If ties are a possibility, then you should provide logic for how to handle this.
If you are using MySQL 8 or later, and have access to ROW_NUMBER, then there is a slick solution here:
SELECT *
FROM
(
SELECT *,
ROW_NUMBER() OVER (ORDER BY weight) rn1,
ROW_NUMBER() OVER (ORDER BY weight DESC) rn2
FROM yourTable
) t
WHERE 1 IN (rn1, rn2);
union all comes to mind:
(select t.*
from t
order by weight asc
limit 1
) union all
(select t.*
from t
order by weight desc
limit 1
);
This should be pretty optimal in terms of performance if you have an index on weight.
This guarantees exactly two rows in the result set, even if there are ties.
The following query is working fine without ',MAX(Row)'
WITH QResult AS
(SELECT
ROW_NUMBER() OVER (ORDER BY Ad_Date DESC) AS Row,
*
FROM [vw_ads]
)
SELECT *, MAX(Row)
FROM QResult
When MAX(Row) is added, SQL Server 2008 is throwing the following error :
Column 'QResult.Row' is invalid in the select list because it is not
contained in either an aggregate function or the GROUP BY clause.
When using an aggregate function like SUM, COUNT or MAX, and you want to also select other columns from your data, then you need to group your data by the other column(s) used in your query.
So you need to write something like:
WITH QResult AS
(SELECT
ROW_NUMBER() OVER (ORDER BY Ad_Date DESC) AS Row,
*
FROM [vw_ads]
)
SELECT Co1l, Col2, MAX(Row)
FROM QResult
GROUP BY Col1, Col2
This also means you need to explicitly spell out the columns you want - a good idea in any case. You cannot use * in a GROUP BY clause.
Update: based on your comment, I guess what you really want is something like this:
(see Update #2 - Martin Smith's suggestion is even better than my original idea here)
WITH QResult AS
(SELECT
ROW_NUMBER() OVER (ORDER BY Ad_Date DESC) AS Row,
*
FROM [vw_ads]
)
SELECT
Co1l, Col2,
MaxRow = (SELECT MAX(Row) FROM QResult)
FROM QResult
This will give you the maximum value of Row from the CTE, the same value, for each row of your result set.
Update #2: Martin Smith's suggestion would be this:
WITH QResult AS
(SELECT
ROW_NUMBER() OVER (ORDER BY Ad_Date DESC) AS Row,
*
FROM [vw_ads]
)
SELECT
Co1l, Col2,
MAX(Row) OVER()
FROM QResult
and of course, this works, too - and even more efficient than my solution. Thanks, Martin!
You will need to decide why you are obtaining MAX(Row). Is it the max row by Ad_Date? Is the max row overall?
If you change it to:
WITH QResult AS (SELECT ROW_NUMBER() OVER (ORDER BY Ad_Date DESC) AS Row,* FROM [vw_ads])
SELECT Ad_Date, MAX(Row) from QResult
GROUP BY Ad_Date
...that will return you the max row by Ad_Date which is what I'm assuming you are looking for.
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Combine multiple results in a subquery into a single comma-separated value
Concat groups in SQL Server
I want to be able to get the duplication's removed
SELECT Count(Data) as Cnt, Id
FROM [db].[dbo].[View_myView]
Group By Data
HAVING Count(Data) > 1
In MySQL it was as simple as this:
SELECT Count(Data), group_concat(Id)
FROM View_myView
Group By Data
Having Cnt > 1
Does anyone know of a solution? Examples are a plus!
In SQL Server as of version 2005 and newer, you can use a CTE (Common Table Expression) with the ROW_NUMBER function to eliminate duplicates:
;WITH LastPerUser AS
(
SELECT
ID, UserID, ClassID, SchoolID, Created,
ROW_NUMBER() OVER(PARTITION BY UserID ORDER BY Created DESC) AS 'RowNum'
FROM dbo.YourTable
)
SELECT
ID, UserID, ClassID, SchoolID, Created,
FROM LastPerUser
WHERE RowNum = 1
This CTE "partitions" your data by UserID, and for each partition, the ROW_NUMBER function hands out sequential numbers, starting at 1 and ordered by Created DESC - so the latest row gets RowNum = 1 (for each UserID) which is what I select from the CTE in the SELECT statement after it.
Using the same CTE, you can also easily delete duplicates:
;WITH LastPerUser AS
(
SELECT
ID, UserID, ClassID, SchoolID, Created,
ROW_NUMBER() OVER(PARTITION BY UserID ORDER BY Created DESC) AS 'RowNum'
FROM dbo.YourTable
)
DELETE FROM dbo.YourTable t
FROM LastPerUser cte
WHERE t.ID = cte.ID AND cte.RowNum > 1
Same principle applies: you "group" (or partition) your data by some criteria, you consecutively number all the rows for each data partition, and those with values larger than 1 for the "partitioned row number" are weeded out by the DELETE.
Just use distinct to remove duplicates. It sounds like you were using group_concat to join duplicates without actually wanting to use its value. In that case, MySQL also has a distinct you could have been using:
SELECT DISTINCT Count(Data) as Cnt, Id
FROM [db].[dbo].[View_myView]
GROUP BY Id
HAVING Count(Data) > 1
Also, you can't group by something you use in an aggregate function; I think you mean to group by id. I corrected it in the example above.