Separating the list of value by comparing frequency - sql

I have the bellow data set output should come like less then one frequency..
i am new to sql so don't have much idea..
in the input i have 3 times 1, 2 times 2, 3 times 3 and 2 times 4. output i want 2 times 1, 1 time 2, 2 times 3 and 1 time 4..
Any suggestion how to achieve this output!!

This can be written in a more compact form, but just for clarity:
With Src As ( --< Source table
Select * From (Values (1),(2),(3),(1),(1),(2),(3),(3),(4),(4),(5)) V (Id)
), Numbers As ( --< Auxiliary table with numbers from 1 to maximum row count of Src
Select ROW_NUMBER() Over (Order By Id) As N From Src
), Counted As ( --< Calculate current number of ID occurances
Select Id, Count(Id) As Cnt From Src Group By Id
)
Select Id
From Counted --< From distinct list of IDs
Inner Join Numbers --< replicate each row
On Numbers.N < Counted.Cnt --< one less time than the Cnt
Expression to replicate the row taken from SQL: Repeat a result row multiple times...
jpw implementation (please feel free to copy it into your own answer):
With Src As ( --< Source table
Select * From (Values (1),(2),(3),(1),(1),(2),(3),(3),(4),(4),(5)) V (Id)
), Numbered As ( --< Number ID occurances
Select Id, row_number() Over (Partition By id Order By id) As n From Src
)
Select Id From Numbered Where n > 1 --< Take one off

Related

Split one row into multiple rows in SQL with amounts divided equally

I have a table that contains ID, an amount column and a count column. For each row I would like to split them into multiple rows, based on the count column. I would then like the amount column to be split evenly between these rows, and create a new id based on the original id and the row count.
This is how the table looks like:
ID Amount Count
1001 8 2
1002 15 3
And this is the desired output
ID Amount
1001-1 4
1001-2 4
1002-1 5
1002-2 5
1002-3 5
Whats the best approach for this?
You can use a recursive CTE. This looks something like:
with recursive cte as (
select id, amount / cnt as amount, cnt, 1 as lev
from t
union all
select id, amount, cnt, lev + 1
from t
where lev < cnt
)
select id || '-' || lev, amount
from cte;
Note that this uses standard syntax; the exact syntax might vary depending on your database.
Unfortunately, Redshift does not support recursive queries.
Here is another option using a temporary table of numbers.
create temp table tmp(n int);
insert into tmp(n) values (1), (2), (3), (4), ...; -- expand as needed
select concat(t.id, '-', p.n) id, t.amount/t.count amount
from mytable t
inner join tmp p on p.n <= t.count
order by t.id, p.n

Put results into a group of 2, or any number I specify

I need a way to put results into # of groups that I specify.
I have tried ntile() function, which I thought would use but it's not working:
WITH CTE AS (
SELECT 1 as Number
UNION ALL
SELECT Number+1
FROM CTE
WHERE Number < 100
)
SELECT *, ntile(80) over (order by number desc) as 'test'
FROM CTE
For the expected results, the Quartile column should output a number for every 2 entries (as specified in NTILE(80)), but it can be 2, 4, 10, or any number I specify.
Maybe NTILE() is not the right function but is there a function that does what I want?
So, if I specify 3, then the result should group every 3 records. If I specify 15, then the result should group every 15 records and move onto next group.
Hope I'm being clear
...should output a number for every 2 entries...
No, you have 100 entries and you want to divide them in 80 groups. You'll get some groups with 1 entry and other groups with 2 entries.
Read the definition of NTILE(). If you want groups with 2 entries you can do it as shown below by dividing it in 50 groups:
WITH recursive
CTE AS (
SELECT 1 as Number
UNION ALL
SELECT Number + 1
FROM CTE
WHERE Number < 100
)
SELECT *,
ntile(50) -- changed here
over (order by number desc) as test
FROM CTE
You didn't say what database engine you are using, so I assumed PostgreSQL.
I think you simply want the modulus operator:
WITH CTE AS (
SELECT 1 as Number
UNION ALL
SELECT Number+1
FROM CTE
WHERE Number < 100
)
SELECT cte.*,
(ROW_NUMBER() OVER (ORDER BY Number DESC) - 1) % 3 -- or however many groups that you want
FROM CTE

How to average the top n in each SQL group

I'm trying to figure out how to average the top N values within each group. I have a table with two columns, Group and Value. My goal is to average the top N values within each group where N is different based on another table.
For group A, N equals 3 and is highlighted in red. The output is the average of the top 3 values.
For group B, N equals 2 and is highlighted in green. Because we only have 1 value of 2.2 for group B, we need to go to the filler table. The filler value for group B is 2.0, so we will average 2.2 and 2.0. If N = 5, then the filler value will be repeated 4 times for Group B.
My initial idea is to:
Rank the values in each group
Join it to the second table
Use where Rank <= N to remove the duplicates before averaging
However, I not sure how the filling table could be incorporated since N could be greater than the number of values I have. I do need to use SQL Server 2008.
First of all, I hope that you're using more adequate names instead of Group and Value. Here's a sample code that first defines the order to later define the N values that will be used and get an average from those. The code is untested as you didn't provide consumable sample data.
WITH CTE AS(
SELECT *,
ROW_NUMBER() OVER( PARTITION BY [Group] ORDER BY [Value] DESC) AS rn,
COUNT(*) OVER( PARTITION BY [Group]) ItemCount
FROM TableWithValues
)
SELECT [Group],
(SUM( [Value]) + CASE WHEN N.n > c.ItemCount
THEN (N.n - c.ItemCount) * F.Filler
ELSE 0 END)/ N.n AS [Value]
FROM CTE c
JOIN TableWithN N ON c.[Group] = N.[Group] AND c.rn <= N.n
JOIN Fillers F ON c.[Group] = F.[Group]
GROUP BY [Group];

Multiply rows in single query

Table1 has the following 2 columns and 4 rows:
Entity Number
------ ------
Car 4
Shop 1
Apple 3
Pear 1
I'd like to have one set based SQL query, which produces the below desired results. Basically duplicating the Entities by the Number of times in the Number column.
I could only do it by loop through the rows one by one, which is not really elegant, neither set based.
Desired result:
Entity
------
Car
Car
Car
Car
Shop
Apple
Apple
Apple
Pear
One method uses recursive CTEs:
with cte as (
select t1.entity, t1.number
from table1 t1
union all
select cte.entity, cte.number - 1
from cte
where cte.number > 0
)
select entity
from cte;
Note: Using the default settings, this is limited to 100 rows per entity. You can use OPTION (MAXRECURSION 0) to get around this.
You can also solve this with a numbers table, but such a problem is a good introduction to recursive CTEs.
Use this
;WITH CTE
AS
(
SELECT
SeqNo = 1,
Entity,
Number
FROM YourTable
UNION ALL
SELECT
SeqNo = SeqNo+1,
Entity,
Number
FROM CTE
WHERE SeqNo < Number
)
SELECT
Entity
FROM CTE
ORDER BY 1
A non-recursion solution, will be using a fixed sequence number, then join the table based on this number like this:
WITH numbers
AS
(
SELECT n
FROM (VALUES(1),(2),(3),(4),(5),(6),(7),(8),(9), (10)) AS numbers(n)
)
SELECT t.Entity
FROM Table1 AS t
INNER JOIN numbers as n ON t.number >= n.n;
This will support up to 10 times duplication, you can add extra numbers to support extra duplication times.
Demo
You can use spt_values as source for numbers table
select EntityList.*
from EntityList
, (
select number as n from master..spt_values WHERE Type = 'P' and Number between 1 and (select max(number) from EntityList)
) t
where n <= number
order by entity

get ROW NUMBER of random records

For a simple SQL like,
SELECT top 3 MyId FROM MyTable ORDER BY NEWID()
how to add row numbers to them so that the row numbers become 1,2, and 3?
UPDATE:
I thought I can simplify my question as above, but it turns out to be more complicated. So here is a fuller version -- I need to give three random picks (from MyTable) for each person, with pick/row number of 1, 2, and 3, and there is no logical joining between person and picks.
SELECT * FROM Person
LEFT JOIN (
SELECT top 3 MyId FROM MyTable ORDER BY NEWID()
) D ON 1=1
The problem with above SQL are,
Obviously, pick/row number of 1, 2, and 3 should be added
and what is not obvious is that, the above SQL will give each person the same picks, whereas I need to give different person different picks
Here is a working SQL to test it out:
SELECT TOP 15 database_id, create_date, cs.name FROM sys.databases
CROSS apply (
SELECT top 3 Row_number()OVER(ORDER BY (SELECT NULL)) AS RowNo,*
FROM (SELECT top 3 name from sys.all_views ORDER BY NEWID()) T
) cs
So, Please help.
NOTE: This is NOT about MySQL byt T-SQL as their syntax are different, Thus the solution is different as well.
Add Row_number to outer query. Try this
SELECT Row_number()OVER(ORDER BY (SELECT NULL)),*
FROM (SELECT TOP 3 MyId
FROM MyTable
ORDER BY Newid()) a
Logically TOP keyword is processed after Select. After Row Number is generated random 3 records will be pulled. So you should not generate Row Number in original query
Update
It can be achieved through CROSS APPLY. Replace the column names inside cross apply where clause with valid column name from Person table
SELECT *
FROM Person p
CROSS apply (SELECT Row_number()OVER(ORDER BY (SELECT NULL)) rn,*
FROM (SELECT TOP 3 MyId
FROM MyTable
WHERE p.some_col = p.some_col -- Replace it with some column from person table
ORDER BY Newid())a) cs