How to do permutation in tsql (set based) - sql

I have the below input
PlayerID MatchPlayed RunsMade
-------- ----------- --------
1 10 200
2 5 100
3 8 24
4 30 50
The output will be
Combined Players Combined Match Played Combined runs Made
---------------- --------------------- ------------------
1 10 200
1,2 15 300
1,3 18 224
1,4 40 250
1,2,3 23 324
1,2,4 45 350
1,3,4 48 274
1,2,3,4 53 374
2 5 100
2,3 13 124
2,4 35 150
2,3,4 43 174
3 8 24
3,4 38 74
4 30 50
The Combined Match Played column is the sum of the values of Match Played column of those players. e.g. for Combined Played 1,2 the Combined Match Played value is 10 + 5 = 15.
similarly, Combined Runs Made is the sum of the Runs MAde column of the individual players. e.g. for the same example, the Combined Runs MAde column is 200 +100 =300.
Thanks

Setup:
create table Input(PlayerId int, MatchPlayed int, RunsMade int)
insert Input
select 1, 10, 200
union all select 2, 5, 100
union all select 3, 8, 24
union all select 4, 30, 50
Query:
with cte(Combined, PlayerId, MatchPlayed, RunsMade)
as
(
select cast(PlayerId as varchar(500)), PlayerId, MatchPlayed, RunsMade
from Input
union all
select cast(cte.Combined + ',' + cast(inp.PlayerId as varchar) as varchar(500)), inp.PlayerId, inp.MatchPlayed + cte.MatchPlayed, inp.RunsMade + cte.RunsMade
from cte
join Input inp on
cte.PlayerId < inp.PlayerId
)
select Combined, MatchPlayed, RunsMade
from cte
order by Combined

Related

Query to create multiple equal records of volume distribution using duration (in terms of months)

Input Output ResultsHope you are doing good.
I am stuck in a requirement where I need to have records distributed into multiple records based on the duration I get it from a linking table.
Suppose I have a volume of 100 and duration I am getting is 20 months linking table then my output should have 20 records of each 5(100/20). Could you please help me with the query how to do this SQL.
The WITH clause is here just to generate some sample data and, as such, it is not a part of the answer.
You can join the tables ON PRODUCT columns, limit the iterations using LEVEL <= DURATION, group the data and show the amount either as Min, Max or Avg of COST/DURATION rounded to two decimals. I put all of the data in the select list. Here is the complete code with the result. Regards...
WITH
t_duration AS
(
Select 'A' "PRODUCT", 10 "DURATION" From Dual Union All
Select 'B' "PRODUCT", 6 "DURATION" From Dual Union All
Select 'C' "PRODUCT", 4 "DURATION" From Dual
),
t_cost AS
(
Select 'A' "PRODUCT", 100 "COST" From Dual Union All
Select 'B' "PRODUCT", 50 "COST" From Dual Union All
Select 'C' "PRODUCT", 40 "COST" From Dual
)
SELECT
LEVEL "MONTH_ORDER_NUMBER",
d.PRODUCT "PRODUCT",
d.DURATION "DURATION",
c.COST "COST",
Round(Avg(c.COST / d.DURATION), 2) "AVG_MONTHLY_AMOUNT",
Round(Max(c.COST / d.DURATION), 2) "MAX_MONTHLY_AMOUNT",
Round(Min(c.COST / d.DURATION), 2) "MIN_MONTHLY_AMOUNT"
FROM
t_duration d
INNER JOIN
t_cost c ON(c.PRODUCT = d.PRODUCT)
CONNECT BY LEVEL <= d.DURATION
GROUP BY
d.PRODUCT, d.DURATION, c.COST, LEVEL
ORDER BY
d.PRODUCT, LEVEL
--
-- R e s u l t
--
-- MONTH_ORDER_NUMBER PRODUCT DURATION COST AVG_MONTHLY_AMOUNT MAX_MONTHLY_AMOUNT MIN_MONTHLY_AMOUNT
-- ------------------ ------- ---------- ---------- ------------------ ------------------ ------------------
-- 1 A 10 100 10 10 10
-- 2 A 10 100 10 10 10
-- 3 A 10 100 10 10 10
-- 4 A 10 100 10 10 10
-- 5 A 10 100 10 10 10
-- 6 A 10 100 10 10 10
-- 7 A 10 100 10 10 10
-- 8 A 10 100 10 10 10
-- 9 A 10 100 10 10 10
-- 10 A 10 100 10 10 10
-- 1 B 6 50 8.33 8.33 8.33
-- 2 B 6 50 8.33 8.33 8.33
-- 3 B 6 50 8.33 8.33 8.33
-- 4 B 6 50 8.33 8.33 8.33
-- 5 B 6 50 8.33 8.33 8.33
-- 6 B 6 50 8.33 8.33 8.33
-- 1 C 4 40 10 10 10
-- 2 C 4 40 10 10 10
-- 3 C 4 40 10 10 10
-- 4 C 4 40 10 10 10
That looks as if ntile would do the job (at least, that's how I understood the question).
Here's a table with 100 rows (that's your "volume of 100").
SQL> create table test (id) as
2 select level from dual connect by level <= 100;
Table created.
You'd then pass 20 (that's "duration of 20 months") to ntile and get the result - see the grp column, having 20 groups, each of them having 5 rows:
SQL> select id, ntile(20) over (order by id) grp
2 from test
3 order by id;
ID GRP
---------- ----------
1 1
2 1
3 1
4 1
5 1
6 2
7 2
8 2
9 2
10 2
11 3
12 3
13 3
14 3
15 3
<snip>
91 19
92 19
93 19
94 19
95 19
96 20
97 20
98 20
99 20
100 20
100 rows selected.
SQL>
[EDIT, based on new information]
With sample tables you posted:
SQL> with
2 duration (product, duration) as
3 (select 'A', 10 from dual union all
4 select 'B', 6 from dual union all
5 select 'C', 4 from dual
6 ),
7 cost (product, cost) as
8 (select 'A', 100 from dual union all
9 select 'B', 50 from dual union all
10 select 'C', 40 from dual
11 )
query would look like this:
12 select d.product,
13 c.cost / d.duration as amount
14 from duration d join cost c on c.product = d.product
15 cross join table(cast(multiset(select level from dual
16 connect by level <= d.duration
17 ) as sys.odcinumberlist))
18 order by d.product;
PRODUCT AMOUNT
---------- ----------
A 10
A 10
A 10
A 10
A 10
A 10
A 10
A 10
A 10
A 10
B 8,33333333
B 8,33333333
B 8,33333333
B 8,33333333
B 8,33333333
B 8,33333333
C 10
C 10
C 10
C 10
20 rows selected.
SQL>

sql sum with separate column for each day

My current code looks like this:
declare #start datetime
declare #end datetime
set #start = '2/16/2020'
set #end = '2/19/2020'
select
s.location, s.department, s.position, SUM(s.hours)/60
from SCHEDULES s where SCHDATE between #start and #end
group by s.location, s.department, s.position
It yields the following results (which is correct):
loc dep pos hrs
2 2 7 96
3 2 11 96
2 2 13 192
3 2 5 96
3 1 4 228
How do I break this out by day so that the format looks like below:
'start' is the #start variable and 'start+1' is simply that plus 1 day, etc.
loc dep pos start start+1 start+2 start+3
2 2 7 24 24 24 24
3 2 11 24 24 24 24
2 2 13 48 48 48 48
3 2 5 24 24 24 24
3 1 4 57 57 57 57
thanks
Sounds like you want to do a pivot:
SELECT *
FROM SCHEDULES s
PIVOT(
SUM(hours)
FOR SCHDATE IN (
[2020-2-16],
[2020-2-17],
[2020-2-18],
[2020-2-19])
) AS pivot_table;
Hopefully the dates you want to work with are fixed and known. If you need to pivot on calculated columns, things seem to get a lot more complicated. For example, see this thread.

Insert number of records based on field value in Oracle

I have the following script :-
SELECT
quoteid,
tariff_length,
cost
FROM
tblquotesnew q
LEFT JOIN
tbltariffsnew t
ON q.tariff_id = t.tariff
which may return something like:-
quoteid tariff_length cost
310 4 12
311 6 16
Is it possible to INSERT rows into a seperate table, where the number of rows inserted is based tariff_length?
So, using the above, the insertion table (tblcommnew) would look like
commid quoteid cost
1 310 12
2 310 12
3 310 12
4 310 12
5 311 16
6 311 16
7 311 16
8 311 16
9 311 16
10 311 16
Here's one option:
SQL> with test (quoteid, tariff_length, cost) as
2 (select 310, 4, 12 from dual union
3 select 311, 6, 16 from dual
4 )
5 select rownum as commid, quoteid, cost
6 from test,
7 table(cast(multiset(select level from dual
8 connect by level <= tariff_length
9 ) as sys.odcinumberlist));
COMMID QUOTEID COST
---------- ---------- ----------
1 310 12
2 310 12
3 310 12
4 310 12
5 311 16
6 311 16
7 311 16
8 311 16
9 311 16
10 311 16
10 rows selected.
SQL>
A slight variation on #Littlefoot's approach is to use an XMLTable to generate the combinations:
with tblquotesnew (quoteid, tariff_length, cost) as (
select 310, 4, 12 from dual
union all select 311, 6, 16 from dual
)
select rownum as commid, quoteid, cost
from tblquotesnew
cross join xmltable ('1 to xs:integer($n)' passing tariff_length as "n");
COMMID QUOTEID COST
---------- ---------- ----------
1 310 12
2 310 12
3 310 12
4 310 12
5 311 16
6 311 16
7 311 16
8 311 16
9 311 16
10 311 16
As an insert you then just do:
insert into tblcommnew (commid, quoteid, cost)
select rownum, quoteid, cost
from tblquotesnew
cross join xmltable ('1 to xs:integer($n)' passing tariff_length as "n");
10 rows inserted.

count of a column in the result set on which distinct is already applied

Consider the table Property.
KeyIdNum|Property|IdNum
1 12 1234
1 12 1234
1 44 1234
1 12 1234
1 56 1234
2 12 4567
3 12 6789
3 56 6789
3 12 6789
4 44 3434
5 12 4444
6 44 9999
6 44 9999
It contains property num associated with each id num.But it contains duplicates.
I applied distinct to avoid duplicates.
select distinct KeyIdNum,Property,IdNum from Property.
So i got the result as :
KeyIdNum |Property |IdNum
1 12 1234
1 44 1234
1 56 1234
2 12 4567
3 12 6789
3 56 6789
4 44 3434
5 12 4444
6 44 9999
But now I want to `select( after applying distinct) ,the KeyIdNum (or IdNum) which are coming more than one time in the distinct result set shown above.
Please help me on this.I am not able to find a way to get the count of a column in the distinct result set using a single query.
Below query will result of KeyidNum , its number of row count.
select KeyIdNum,count(KeyIdNum)
From (
select distinct KeyIdNum,Property,IdNum from Property )
group by KeyIdNum
select KeyIdNum,count(KeyIdNum) as count
From (
select distinct KeyIdNum,Property,IdNum from Table19 )A
group by KeyIdNum
output
KeyIdNum count
1 3
2 1
3 2
4 1
5 1
6 1
This answer uses t-sql:
SELECT x
FROM ( SELECT * ,
rn = rownumber() OVER ( PARTITION BY keyidnum, idnum
ORDER BY keyidnum, idnum )
FROM tblProperty
) x
WHERE rn > 1

Exclude the specific kind of record

I am using SQL Server 2008 R2. I do have records as below in a table :
Id Sys Dia Type UniqueId
1 156 20 first 12345
2 157 20 first 12345
3 150 15 last 12345
4 160 17 Average 12345
5 150 15 additional 12345
6 157 35 last 891011
7 156 25 Average 891011
8 163 35 last 789521
9 145 25 Average 789521
10 156 20 first 963215
11 150 15 last 963215
12 160 17 Average 963215
13 156 20 first 456878
14 157 20 first 456878
15 150 15 last 456878
16 160 17 Average 456878
17 150 15 last 246977
18 160 17 Average 246977
19 150 15 additional 246977
Regarding this data, these records are kind of groups that have common UniqueId. The records can be of type "first, last, average and additional". Now, from these records I want to select "average" type of records only if they have "first" or "additional" kind of reading in group. Else I want to exclude them from selection..
The expected result is :
Id Sys Dia Type UniqueId
1 156 20 first 12345
2 157 20 first 12345
3 150 15 last 12345
4 160 17 Average 12345
5 150 15 additional 12345
6 157 35 last 891011
7 163 35 last 789521
8 156 20 first 963215
9 150 15 last 963215
10 160 17 Average 963215
11 156 20 first 456878
12 157 20 first 456878
13 150 15 last 456878
14 160 17 Average 456878
15 150 15 last 246977
16 160 17 Average 246977
17 150 15 additional 246977
In short, I don't want to select the record that have type="Average" and have only "last" type of record with same UniqueId. Any solution?
Using EXISTS operator along correlated sub-query:
SELECT * FROM dbo.Table1 t1
WHERE [Type] != 'Average'
OR EXISTS (SELECT * FROM Table1 t2
WHERE t1.UniqueId = t2.UniqueId
AND t1.[Type] = 'Average'
AND t2.[Type] IN ('first','additional'))
SQLFiddle DEMO
Try something like this:
SELECT * FROM MyTable WHERE [Type] <> 'Average'
UNION ALL
SELECT * FROM MyTable T WHERE [Type] = 'Average'
AND EXISTS (SELECT * FROM MyTable
WHERE [Type] IN ('first', 'additional')
AND UniqueId = T.UniqueId)
The first SELECT statement gets all records except the ones with Type = 'Average'. The second SELECT statement gets only the Type = 'Average' records that have at least one record with the same UniqueId, that is of type 'first' or 'additional'.