TSQL, Remove rows based upon Row-Index - sql-server

My Table is like this.....
**AttName** **Title** **Count_Index**
Red Boys 1
Red Girls 2
Green Boys 1
Blue Boys 1
I only Want to return...
Red Boys 1
Red Girls 2
Thats because I have Red with two entries, I want to skip/remove all the ROW(s) if their Count is ONLY 1. In other words I am only interested in rows if their count goes above "1".

Try
SELECT *
FROM table1
WHERE AttName IN (SELECT AttName FROM table1 GROUP BY AttName HAVING COUNT(*) > 1)
SQLFiddle
Output
| ATTNAME | TITLE | COUNT_INDEX |
---------------------------------
| Red | Boys | 1 |
| Red | Girls | 2 |

Ok, this is tested. I like using windowing functions when looking for things like duplicates. Particularly because the avoids doing a subselect in a where clause, and from the same table twice. Instead all the needed columns are already pulled in the subselect. Although windowing function can be expensive sometimes.
Select *, ROW_NUMBER() over (Partition by AttrName Order By AttrName) --probably better to order by whatever the primary key is for consistent results, esepcially if you plan to use this in a delete statement
From (
SELECT AttName, title, COUNT(AttrName) over (partition by AttrName) as cnt
FROM yourtable
) as counted
Where counted.cnt > 1

Related

sql selection of one value from several identical

I have the result of executing a query. it collects data from several tables. he is such a:
|Name|date |number|Id
|alex|01-01-2021 |1111 | 1
|mike|01-01-2021 |2222 | 2
|alex|02-01-2021 |1111 | 3
|alex|03-01-2021 |1111 | 4
|john|04-01-2021 |3333 | 5
i need to get the following result:
|Name|date |number| Id
|mike|01-01-2021|2222 | 2
|alex|any value |1111 | Any value
|john|04-01-2021|3333 | 5
I need to select one of the repeated values and show it.I have a large query with many columns. here I gave only a short version to explain the essence of the problem
select Name,max(date) as date,number
from atable
group by Name, number
You may use this CTE and manage which date (first or last) you will get
WITH data AS (
SELECT
Name,
date,
number,
row_number() OVER (PARTITION BY Name ORDER BY date) AS row_num
FROM test01
)
SELECT
Name,
date,
number
FROM data
WHERE row_num = 1

Using STRING_SPLIT for 2 columns in a single table

I've started from a table like this
ID | City | Sales
1 | London,New York,Paris,Berlin,Madrid| 20,30,,50
2 | Istanbul,Tokyo,Brussels | 4,5,6
There can be an unlimited amount of cities and/or sales.
I need to get each city and their salesamount their own record. So my result should look something like this:
ID | City | Sales
1 | London | 20
1 | New York | 30
1 | Paris |
1 | Berlin | 50
1 | Madrid |
2 | Istanbul | 4
2 | Tokyo | 5
2 | Brussels | 6
What I got so far is
SELECT ID, splitC.Value, splitS.Value
FROM Table
CROSS APLLY STRING_SPLIT(Table.City,',') splitC
CROSS APLLY STRING_SPLIT(Table.Sales,',') splitS
With one cross apply, this works perfectly. But when executing the query with a second one, it starts to multiply the number of records a lot (which makes sense I think, because it's trying to split the sales for each city again).
What would be an option to solve this issue? STRING_SPLIT is not neccesary, it's just how I started on it.
STRING_SPLIT() is not an option, because (as is mentioned in the documantation) the output rows might be in any order and the order is not guaranteed to match the order of the substrings in the input string.
But you may try with a JSON-based approach, using OPENJSON() and string transformation (comma-separated values are transformed into a valid JSON array - London,New York,Paris,Berlin,Madrid into ["London","New York","Paris","Berlin","Madrid"]). The result from the OPENJSON() with default schema is a table with columns key, value and type and the key column is the 0-based index of each item in this array:
Table:
CREATE TABLE Data (
ID int,
City varchar(1000),
Sales varchar(1000)
)
INSERT INTO Data
(ID, City, Sales)
VALUES
(1, 'London,New York,Paris,Berlin,Madrid', '20,30,,50'),
(2, 'Istanbul,Tokyo,Brussels', '4,5,6')
Statement:
SELECT d.ID, a.City, a.Sales
FROM Data d
CROSS APPLY (
SELECT c.[value] AS City, s.[value] AS Sales
FROM OPENJSON(CONCAT('["', REPLACE(d.City, ',', '","'), '"]')) c
LEFT OUTER JOIN OPENJSON(CONCAT('["', REPLACE(d.Sales, ',', '","'), '"]')) s
ON c.[key] = s.[key]
) a
Result:
ID City Sales
1 London 20
1 New York 30
1 Paris
1 Berlin 50
1 Madrid NULL
2 Istanbul 4
2 Tokyo 5
2 Brussels 6
STRING_SPLIT has no context of what oridinal positions are. In fact, the documentation specifically states that it doesn't care about it:
The order of the output may vary as the order is not guaranteed to match the order of the substrings in the input string.
As a result, you need to use something that is aware of such basic things, such as DelimitedSplit8k_LEAD.
Then you can do something like this:
WITH Cities AS(
SELECT ID,
DSc.Item,
DSc.ItemNumber
FROM dbo.YourTable YT
CROSS APPLY dbo.DelimitedSplit8k_LEAD(YT.City,',') DSc)
Sales AS(
SELECT ID,
DSs.Item,
DSs.ItemNumber
FROM dbo.YourTable YT
CROSS APPLY dbo.DelimitedSplit8k_LEAD(YT.Sales,',') DSs)
SELECT ISNULL(C.ID,S.ID) AS ID,
C.Item AS City,
S.Item AS Sale
FROM Cities C
FULL OUTER JOIN Sales S ON C.ItemNumber = S.ItemNumber;
Of course, however, the real solution is fix your design. This type of design is going to only cause you 100's of problems in the future. Fix it now, not later; you'll reap so many rewards sooner the earlier you do it.

SQL: doing several joins to evaluate different columns

I'm quite new to SQL but use it a lot now in my work now (Microsoft SQL Server).
So the issue is this: I collect data that is atypical for a certain column.
Let's say I got different Burgers and they should have a standardized calories value. So I did this with a query
------------------------------------------
| Burger | calories | numBurgers | Rank |
------------------------------------------
| Chicken| 600 | 20 | 1 |
| Chicken| 400 | 3 | 2 |
| Beef | 700 | 35 | 1 |
| Beef | 850 | 4 | 2 |
-------------------------------------------
To get a list of all the "wrong" burgers I use a temporary table and filter out GroupRank = 1
USE database;
GO
WITH GapRanking AS
(
SELECT TOP 100 PERCENT Burger, calories, COUNT(calories),
ROW_NUMBER() OVER(PARTITION BY Burger ORDER BY COUNT(calories) DESC) AS Rank
)
SELECT * FROM GapRanking
WHERE Rank <> 1
...
I get all combinations of Burgers and calories that are not "standard"
Then I do an Inner Join with the original table and all columns on the one above.
SELECT * FROM BaseTable as base
INNER JOIN
(SELECT * FROM GapRanking
WHERE Rank <> 1) AS err
ON (base.Burgers = err.Burgers
AND base.calories = err.calories)
This way I get a table with complete information about the "not-standard" burgers. So far so good.
Now I want to add other rows where there is a deviation in another criteria, price for example, not just calories and add it to the list if its not already there.
So I thought of UNION or JOIN.
So what is the best approach. UNION the above query with the same query just different column (price instead of calories)?
Or do a JOIN with the same query just different column (price instead of calories)?
The code gets quite "ugly" and I'm not sure if I do the right approach here.
Also because of me using the temporary table using WITH a UNION does not seem possible so easily.
I'm really glad for any ideas here. Cheers
use sub-query and join below is just sudo-code not actual you can follow like this way
select t1.*, t2.required_colum
(SELECT TOP 100 PERCENT Burger, calories, COUNT(calories),
ROW_NUMBER() OVER(PARTITION BY Burger ORDER BY COUNT(calories) DESC) AS Rank
) as t1
join
(SELECT TOP 100 PERCENT Burger, calories, COUNT(calories),
ROW_NUMBER() OVER(PARTITION BY Burger ORDER BY COUNT(calories) DESC) AS Rank
) as t2
on t1.colname = t2.colname
where t1.Rank != 1 and t2.Rank != 1

TSQL Conditional Where or Group By?

I have a table like the following:
id | type | duedate
-------------------------
1 | original | 01/01/2017
1 | revised | 02/01/2017
2 | original | 03/01/2017
3 | original | 10/01/2017
3 | revised | 09/01/2017
Where there may be either one or two rows for each id. If there are two rows with same id, there would be one with type='original' and one with type='revised'. If there is one row for the id, type will always be 'original'.
What I want as a result are all the rows where type='revised', but if there is only one row for a particular id (thus type='original') then I want to include that row too. So desired output for the above would be:
id | type | duedate
1 | revised | 02/01/2017
2 | original | 03/01/2017
3 | revised | 09/01/2017
I do not know how to construct a WHERE clause that conditionally checks whether there are 1 or 2 rows for a given id, nor am I sure how to use GROUP BY because the revised date could be greater than or less than than the original date so use of aggregate functions MAX or MIN don't work. I thought about using CASE somehow, but also do not know how to construct a conditional that chooses between two different rows of data (if there are two rows) and display one of them rather than the other.
Any suggested approaches would be appreciated.
Thanks!
you can use row number for this.
WITH T AS
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY ID ORDER BY Type DESC) AS RN
FROM YourTable
)
SELECT *
FROM T
WHERE RN = 1
Is something like this sufficient?
SELECT *
FROM mytable m1
WHERE type='revised'
or 1=(SELECT COUNT(*) FROM mytable m2 WHERE m2.id=m1.id)
You could use a subquery to take the MAX([type]). In this case it works for [type] since alphabetically we want revised first, then original and "r" comes after "o" in the alphabet. We can then INNER JOIN back on the same table with the matching conditions.
SELECT T2.*
FROM (
SELECT id, MAX([type]) AS [MAXtype]
FROM myTABLE
GROUP BY id
) AS dT INNER JOIN myTable T2 ON dT.id = T2.id AND dT.[MAXtype] = T2.[type]
ORDER BY T2.[id]
Gives output:
id type duedate
1 revised 2017-02-01
2 original 2017-03-01
3 revised 2017-09-01
Here is the sqlfiddle: http://sqlfiddle.com/#!6/14121f/6/0

Selecting Nth child from join table

I've got a TSQL query, a basic one-to-many type association which looks something like this:
SELECT Palette.Palette_ID
Colour.Name
FROM Palette
INNER JOIN Colour
ON Palette.Palette_ID = Colour.Palette_ID
This results in a fairly standard dataset like so, whereby each palette can have an unknown number of colours related to it, maybe none, maybe 20, maybe more:
Palette ID | Colour Name
========================
1 | Red
1 | Blue
2 | Yellow
2 | Red
2 | Orange
3 | Pink
4 | Red
4 | Yellow
I'm looking to write a query which gives me a somewhat pivoted dataset, with 3 columns which give me the first 3 colour accocicated with a Pallette, something like this:
Palette ID | First Colour | Second Colour | Third Colour
========================================================
1 | Red | Blue | NULL
2 | Yellow | Red | Orange
3 | Pink | NULL | NULL
4 | Red | Yellow | NULL
I understand how I might be able to approach this using a GROUP BY and SELECT MIN() MAX() in order to get the first and last colour choices for a given palette, however I'm not sure how to go about selecting the nth joined row like this.
Any advice would be greatly appreciated. I've spent too much time using ORMs and I'm completely out of touch with SQL.
Use a ROW_NUMBER() function:
http://msdn.microsoft.com/en-GB/library/ms186734.aspx
In your query, something like
SELECT Palette.Palette_ID,
Colour.Name,
ROW_NUMBER ( )
OVER ( PARTITION BY Palette.Palette_ID ORDER BY Colour.Name ) row_num
FROM Palette
INNER JOIN Colour
ON Palette.Palette_ID = Colour.Palette_ID
This should add a sequential number to each of the rows for each palette, re-initialising when the palette changes. In your case you don't appear to be ordering by colour name, so you may need to tweak the order by clause.
How about the following - uses the ROW_NUMBER function.
WITH c AS (
SELECT
Palette_ID,
Name,
ROW_NUMBER OVER (PARTITION BY Palette_ID ORDER BY Name) AS rn
FROM Colour
)
SELECT
p.Palette_ID,
c1.Name AS [First Colour],
c2.Name AS [Second Colour],
c3.Name AS [Third Colour]
FROM Palette AS p
LEFT JOIN c AS c1 ON p.Palette_ID = c1.Palette_ID AND c1.rn = 1
LEFT JOIN c AS c2 ON p.Palette_ID = c2.Palette_ID AND c2.rn = 2
LEFT JOIN c AS c3 ON p.Palette_ID = c3.Palette_ID AND c3.rn = 3
Use Pivot to transpose the rows to columns.
CREATE TABLE #test1
(Palette_ID INT,Colour_Name VARCHAR(50))
INSERT #test1
VALUES (1,'Red'),(1,'Blue'),(2,'Yellow'),
(2,'Red'),(2,'Orange'),(3,'Pink'),
(4,'Red'),(4,'Yellow')
SELECT Palette_ID,
[1 color] First_Color,
[2 color] Second_Color,
[3 color] Third_Color
FROM (SELECT CONVERT(VARCHAR(10), Row_number() OVER (partition BY Palette_ID ORDER BY Colour_Name))
+ ' Color' AS dd,
*
FROM #test1) a
PIVOT (Max(Colour_Name)
FOR dd IN([1 color],
[2 color],
[3 color]))piv

Resources