How to join tables, concatenate some data - sql

I have two tables:
1. indexes and quantity of indexes
2. indexes and quantity of indexes with specified boxcodes. Boxcode is a number of box, which box contains indexes.
1. input table 1
item_id quantity
1 10
2 15
3 5
1 5
1 5
2 5
3 5
sum:
1 - 20
2 - 20
3 - 10
2. input table 2
item_id quantity boxcode
1 3 abc
2 2 abc
1 8 def
3 10 ghi
1 9 ghi
2 9 def
2 8 ghi !!!!!!!
1 item_id once on 1 boxcode
I want to get result:
3. result
item_id quantity boxcodes
1 10 abc/3, def/7
2 15 abc/2, def/9, ghi/4
3 5 ghi/5
1 5 def/1, ghi/4
1 5 ghi/5
2 5 ghi/4 !!!!!!!!
3 5 ghi/5
Records from table 1 must be in the same order.
I have no idea how it can be done.
Any suggestion?
CREATE TABLE #input1
(
rownum int,
item_id int,
quantity int
)
CREATE TABLE #input2
(
item_id int,
quantity int,
boxcode varchar(10)
)
INSERT INTO #input1 VALUES (1,1,10)
INSERT INTO #input1 VALUES (2,2,15)
INSERT INTO #input1 VALUES (3,3,5)
INSERT INTO #input1 VALUES (4,1,5)
INSERT INTO #input1 VALUES (5,1,5)
INSERT INTO #input1 VALUES (6,2,5)
INSERT INTO #input1 VALUES (7,3,5)
INSERT INTO #input2 VALUES (1,3, 'abc')
INSERT INTO #input2 VALUES (2,2, 'abc')
INSERT INTO #input2 VALUES (1,8, 'def')
INSERT INTO #input2 VALUES (3,10, 'ghi')
INSERT INTO #input2 VALUES (1,9, 'ghi')
INSERT INTO #input2 VALUES (2,9, 'def')
INSERT INTO #input2 VALUES (2,8, 'ghi')
select * from #input1
select * from #input2
drop table #input1
drop table #input2
result
Thanks,

Weird, but it works:
;WITH rec1 AS (
SELECT rownum,
item_id,
1 as q,
1 as [Level],
quantity
from #input1
UNION ALL
SELECT r.rownum,
r.item_id,
1,
[Level] + 1,
i.quantity
FROM rec1 r
INNER JOIN #input1 i
ON r.rownum = i.rownum AND r.item_id = i.item_id
WHERE [Level] < i.quantity
), rec2 AS (
SELECT boxcode,
item_id,
1 as q,
1 as [Level],
quantity
from #input2
UNION ALL
SELECT r.boxcode,
r.item_id,
1,
[Level] + 1,
i.quantity
FROM rec2 r
INNER JOIN #input2 i
ON r.boxcode = i.boxcode AND r.item_id = i.item_id
WHERE [Level] < i.quantity
), cte1 AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY item_id ORDER BY item_id, rownum) as rn
FROM rec1
), cte2 AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY item_id ORDER BY item_id, boxcode) as rn
FROM rec2
), final AS (
SELECT c1.rownum,
c1.item_id,
c1.quantity,
c2.boxcode+'/'+CAST(SUM(c2.q) as nvarchar(10)) as boxcodes
FROM cte1 c1
INNER JOIN cte2 c2
ON c1.item_id = c2.item_id and c1.rn = c2.rn
GROUP BY c1.rownum, c1.item_id, c1.quantity, c2.boxcode
)
SELECT DISTINCT
f.rownum,
f.item_id,
f.quantity,
STUFF((
SELECT ', '+f1.boxcodes
FROM final f1
WHERE f1.rownum = f.rownum
AND f1.item_id = f.item_id
AND f1.quantity = f.quantity
FOR XML PATH('')
),1,2,'') boxcodes
FROM final f
Output for dataset you have provided:
rownum item_id quantity boxcodes
1 1 10 abc/3, def/7
2 2 15 abc/2, def/9, ghi/4
3 3 5 ghi/5
4 1 5 def/1, ghi/4
5 1 5 ghi/5
6 2 5 ghi/4
7 3 5 ghi/5
The main idea is to spread quantity in both tables for a small parts 1. Than add row number, then join and get result.

A solution (but it's totally based on gofr1's answer, to be honest !), to simplify a bit, would be to create a Numbers table, with as many numbers as you want.
CREATE TABLE Numbers(Number INT PRIMARY KEY);
INSERT Numbers
SELECT TOP 1000 ROW_NUMBER() OVER (ORDER BY name)
FROM sys.all_columns;
That would just avoid the 2 recursive CTEs.
You could then use the same logic as gofr1 :
with rec1 AS (
SELECT
ROW_NUMBER() OVER (PARTITION BY item_id ORDER BY item_id, rownum) as rn,
rownum,
item_id,
case when quantity = 0 then 0 else 1 end as q,
quantity
from #input1
join Numbers n on n.Number <= quantity
)
, rec2 AS (
SELECT
ROW_NUMBER() OVER (PARTITION BY item_id ORDER BY item_id, boxcode) as rn,
boxcode,
item_id,
case when quantity = 0 then 0 else 1 end as q,
quantity
from #input2
join Numbers n on n.Number <= quantity
),
final AS (
SELECT c1.rownum,
c1.item_id,
c1.quantity,
c2.boxcode+'/'+CAST(SUM(c2.q) as nvarchar(10)) as boxcodes
FROM rec1 c1
INNER JOIN rec2 c2
ON c1.item_id = c2.item_id and c1.rn = c2.rn
GROUP BY c1.rownum, c1.item_id, c1.quantity, c2.boxcode
),
stuffed as (
SELECT
distinct rownum,
f.item_id,
f.quantity,
STUFF((
SELECT ', '+f1.boxcodes
FROM final f1
WHERE f1.rownum = f.rownum
AND f1.item_id = f.item_id
AND f1.quantity = f.quantity
FOR XML PATH('')
),1,2,'') boxcodes
FROM final f
group by item_id, quantity, boxcodes, rownum)
select *
from stuffed
order by rownum

Related

Randomly join tables and return columns

I have these 3 tables:
CREATE TABLE DimEmployee(EmployeeID INT)
CREATE TABLE DimDepartment(DepartmentID INT)
CREATE TABLE DimDocteur(PositionID INT)
INSERT INTO DimEmployee(EmployeeID) VALUES (1),(2),(3)
INSERT INTO DimDepartment(DepartmentID) VALUES (1),(5),(6)
INSERT INTO DimPosition(PositionID) VALUES (7),(8),(9)
I want to randomly join the 3 tables and get output like below : (example)
First execute:
EmployeeID DepartmentID PositionID RandomDate
1 4 7 2020-07-24 00:00:00.000
2 5 9 2020-11-25 00:00:00.000
Second execute:
EmployeeID DepartmentID PositionID RandomDate
1 4 7 2020-05-04 00:00:00.000
2 5 9 2020-10-30 00:00:00.000
If you want a random join :
SELECT DP.EmployeeID, Q.Department INTO #T1
FROM DimEmployee AS DP
CROSS APPLY (SELECT TOP 1 Dd.DepartmentID FROM DimDepartment AS DD
ORDER BY NEWID() ) AS Q
SELECT *
INTO #T2
FROM #T1 AS T
CROSS APPLY (SELECT TOP 1 DP.PositionID FROM DimPosition AS DP
ORDER BY NEWID() ) AS Q
Or if you want all possibilities :
SELECT
a.EmployeeID, b.DepartmentID, c.PositionID
FROM
DimEmployee AS a
CROSS JOIN
DimDepartment AS b
CROSS JOIN
DimPosition AS c
You need to row-number each table and join on row-number:
CREATE TABLE DimEmployee(EmployeeID INT)
CREATE TABLE DimDepartment(DepartmentID INT)
CREATE TABLE DimDocteur(PositionID INT)
SELECT
emp.EmployeeID,
dep.DepartmentID,
doc.PositionID,
DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0) RandomDate
FROM (
SELECT *, ROW_NUMBER() OVER(ORDER BY (SELECT 1)) rn
FROM DimEmployee
) emp
JOIN (
SELECT *, ROW_NUMBER() OVER(ORDER BY (SELECT 1)) rn
FROM DimDepartment
) dep ON dep.rn = emp.rn
JOIN (
SELECT *, ROW_NUMBER() OVER(ORDER BY (SELECT 1)) rn
FROM DimDocteur
) doc ON doc.rn = emp.rn
You can also change the ORDER BY to ORDER BY NEWID() to get a more random ordering.

How to merge two particular rows into single row and reaming rows are same using stored procedure SQL Server 2012

I have data like this. first row of Id 1 from particular time period and second row of id 1 is another time period. so now want to combined id and name which are same in the two time periods reaming are same.if there is no orders from that time period its should be display 0 or null.
Id Name Qty Price
----------------------
1 Rose 4 540
1 Rose 1 640
2 Lilly 5 550
2 Lilly 18 360
3 Grand 2 460
3 Grand 10 360
4 lotus 0 0
4 Lotus 9 580
now I want data like this..
Id Name Qty Price
4 540
1 rose
1 640
5 550
2 Lilly
18 360
2 460
3 Grand
10 360
0 0
4 Lotus
9 580
This is my procedure
create PROCEDURE [dbo].[Sp_Orders]
(
#Startdate varchar(30),
#Enddate varchar(30),
#Startdate1 varchar(30),
#Enddate1 varchar(30)
)
--[Sp_Orders] '03/01/2016','03/15/2016','02/01/2016','02/28/2016'
AS
BEGIN
---First Duration----
SELECT DISTINCT
op.ProductId as id, op.Price as Prc,
sc.SubCategoryName as ScName,
COUNT(op.ProductId) AS Qty,
ROUND(SUM(op.Price * op.Quantity), 0) AS Revenue,
FROM
orderdetails od
INNER JOIN
(SELECT DISTINCT
Orderid, Productid, ProductFeatures, Price, Quantity
FROM
OrderProducts) op ON od.Orderid = op.Orderid
INNER JOIN
products p ON p.productid = op.productid
INNER JOIN
subcategory sc ON sc.subcategoryid = p.subcategoryid
WHERE
CONVERT(datetime, CONVERT(varchar(50), od.DeliveryDate, 101)) BETWEEN #Startdate AND #Enddate
GROUP BY
op.ProductID, op.Price, sc.SubCategoryName
---Second Duration----
SELECT DISTINCT
op.ProductID AS id, op.Price AS Prc,
sc.SubCategoryName AS ScName,
COUNT(op.ProductId) AS Qty,
ROUND(SUM(op.Price * op.Quantity), 0) AS Revenue,
FROM
orderdetails od
INNER JOIN
(SELECT DISTINCT
Orderid, Productid, ProductFeatures, Price, Quantity
FROM
OrderProducts) op ON od.Orderid = op.Orderid
INNER JOIN
products p ON p.productid = op.productid
INNER JOIN
subcategory sc ON sc.subcategoryid = p.subcategoryid
WHERE
CONVERT(datetime, CONVERT(varchar(50),od.DeliveryDate,101)) BETWEEN #Startdate1 AND #Enddate1
GROUP BY
op.ProductID, op.Price, sc.SubCategoryName
END
From what I understood from your Question and Comments:
Schema for your case
SELECT * INTO #TAB FROM(
SELECT 1 ID, 'ROSE' NAME, 4 QTY, 540 PRICE
UNION ALL
SELECT 1 , 'ROSE' , 1 , 640
UNION ALL
SELECT 2 , 'LILLY' , 5 , 550
UNION ALL
SELECT 2 , 'LILLY' , 18 ,360
UNION ALL
SELECT 3 , 'GRAND' , 2 , 460
UNION ALL
SELECT 3 , 'GRAND' , 10 ,360
UNION ALL
SELECT 4 , NULL,NULL,NULL
UNION ALL
SELECT 4 , 'LOTUS' , 9 , 580
) AS A
And the Logic to display is as below
SELECT CASE WHEN SNO=1 THEN CAST(ID AS VARCHAR(250)) ELSE '' END ID,
CASE WHEN SNO=1 THEN ISNULL(NAME,'') ELSE '' END NAME,ISNULL(Qty,0)Qty
,ISNuLL(Price,0)Price FROM (
SELECT ROW_NUMBER() Over(partition by Name, Id ORDER BY (SELECT 1)) SNO
,ID, NAME , Qty, Price, ID AS ID2 FROM #TAB
)AS A
ORDER BY ID2, NAME DESC
Try this from your Procedure. And may need to do type cast based on your actual datatypes
CREATE PROCEDURE [DBO].[SP_ORDERS]
(
#STARTDATE VARCHAR(30),
#ENDDATE VARCHAR(30),
#STARTDATE1 VARCHAR(30),
#ENDDATE1 VARCHAR(30)
)
--[SP_ORDERS] '03/01/2016','03/15/2016','02/01/2016','02/28/2016'
AS
BEGIN
SELECT CASE WHEN SNO=1 THEN CAST(ID AS VARCHAR(250)) ELSE '' END ID,CASE WHEN SNO=1 THEN ISNULL(SCNAME,'') ELSE '' END NAME,ISNULL(QTY,0)QTY,ISNULL(REVENUE,0)PRICE FROM (
SELECT ROW_NUMBER() OVER(PARTITION BY SCNAME, ID ORDER BY (SELECT 1)) SNO, ID, SCNAME , QTY, REVENUE, ID AS ID2 FROM (
SELECT DISTINCT OP.PRODUCTID AS ID,OP.PRICE AS PRC,SC.SUBCATEGORYNAME AS SCNAME,COUNT(OP.PRODUCTID) AS QTY, ROUND(SUM(OP.PRICE * OP.QUANTITY), 0) AS REVENUE
FROM ORDERDETAILS OD INNER JOIN
(SELECT DISTINCT ORDERID,PRODUCTID,PRODUCTFEATURES,PRICE,QUANTITY FROM ORDERPRODUCTS ) OP ON OD.ORDERID=OP.ORDERID
INNER JOIN PRODUCTS P ON P.PRODUCTID=OP.PRODUCTID
INNER JOIN SUBCATEGORY SC ON SC.SUBCATEGORYID=P.SUBCATEGORYID
WHERE CONVERT(DATETIME,CONVERT(VARCHAR(50),OD.DELIVERYDATE,101)) BETWEEN #STARTDATE AND #ENDDATE
GROUP BY OP.PRODUCTID,OP.PRICE,SC.SUBCATEGORYNAME
---SECOND DURATION----
UNION ALL --ADDED NOW
SELECT DISTINCT OP.PRODUCTID AS ID,OP.PRICE AS PRC,SC.SUBCATEGORYNAME AS SCNAME,COUNT(OP.PRODUCTID) AS QTY, ROUND(SUM(OP.PRICE * OP.QUANTITY), 0) AS REVENUE
FROM ORDERDETAILS OD INNER JOIN
(SELECT DISTINCT ORDERID,PRODUCTID,PRODUCTFEATURES,PRICE,QUANTITY FROM ORDERPRODUCTS ) OP ON OD.ORDERID=OP.ORDERID
INNER JOIN PRODUCTS P ON P.PRODUCTID=OP.PRODUCTID
INNER JOIN SUBCATEGORY SC ON SC.SUBCATEGORYID=P.SUBCATEGORYID
WHERE CONVERT(DATETIME,CONVERT(VARCHAR(50),OD.DELIVERYDATE,101)) BETWEEN #STARTDATE1 AND #ENDDATE1
GROUP BY OP.PRODUCTID,OP.PRICE,SC.SUBCATEGORYNAME
)
AS A
)B
ORDER BY ID2, NAME
END
Based on your sample data i have given this Out put but if the data is inconsistent it may not give accurate results if you see the Expected Output it gives exact same
Declare #Table1 TABLE
(Id VARCHAR(10), Name varchar(5),Qty VARCHAR(10), Price varchar(10))
;
INSERT INTO #Table1
(Id, Name,Qty, Price)
VALUES
(1, 'Rose',4, 540),
(1, 'Rose',1, 640),
(2, 'Lilly',5, 550),
(2, 'Lilly',18, 360),
(3, 'Grand',2, 460),
(3, 'Grand',10, 360),
(4,'Lotus',0,0),
(4, 'Lotus',9, 580)
;
SCRIPT
;WITH CTE AS (
Select
CASE WHEN RN = 1 THEN ID ELSE NULL END ID,
CASE WHEN RN = 1 THEN Name ELSE NULL END NAME,
Qty,
Price
from (
select
Id,
Name,
Qty,
Price,
ROW_NUMBER()OVER(PARTITION BY ID,NAME ORDER BY NAME)RN
FROM
#Table1)T)
Select CASE WHEN RN = 2 THEN T.Id ELSE '' END ID,
CASE WHEN RN = 2 THEN T.Name ELSE '' END Name,
CASE WHEN RN IN (1,3) THEN ISNULL(T.Qty,0) ELSE '' END qty,
CASE WHEN RN IN (1,3) THEN ISNULL(T.Price,0) ELSE '' END qty from (
Select
T.ID,
T.NAME,
c.Qty,
C.Price,
ROW_NUMBER()OVER(PARTITION BY T.ID,T.NAME ORDER BY T.NAME)RN
from #Table1 T
INNER JOIN CTE C
ON T.Id = C.ID
AND T.Name = C.NAME
OR (T.Qty = C.Qty OR T.Price = C.Price ))T
WHERE T.RN <> 4

How to use aggregate function in update in SQL server 2012

I Tried as shown below:
CREATE TABLE #TEMP
(
ID INT,
EmpID INT,
AMOUNT INT
)
INSERT INTO #TEMP VALUES(1,1,10)
INSERT INTO #TEMP VALUES(2,1,5)
INSERT INTO #TEMP VALUES(3,2,6)
INSERT INTO #TEMP VALUES(4,3,8)
INSERT INTO #TEMP VALUES(5,3,10)
.
.
.
SELECT * FROM #TEMP
ID EmpID AMOUNT
1 1 10
2 1 5
3 2 6
4 3 8
5 4 10
UPDATE #TEMP
SET AMOUNT = SUM(AMOUNT) - 11
Where EmpID = 1
Expected Output:
Table consists of employeeID's along with amount assigned to Employee I need to subtract amount from amount filed depending on employee usage. Amount "10" should be deducted from ID = 1 and amount "1" should be deducted from ID = 2.
Amount: Credits available for that particular employee depending on date.
So i need to reduce credits from table depending on condition first i need to subtract from old credits. In my condition i need to collect 11 rupees from empID = 1 so first i need to collect 10 rupee from ID=1 and 1 rupee from the next credit i.e ID=2. For this reason in my expected output for ID=1 the value is 0 and final output should be like
ID EmpID AMOUNT
1 1 0
2 1 4
3 2 6
4 3 8
5 4 10
Need help to update records. Check error in my update statement.
Declare #Deduct int = -11,
#CurrentDeduct int = 0 /*this represent the deduct per row */
update #TEMP
set #CurrentDeduct = case when abs(#Deduct) >= AMOUNT then Amount else abs(#Deduct) end
, #Deduct = #Deduct + #CurrentDeduct
,AMOUNT = AMOUNT - #CurrentDeduct
where EmpID= 1
I think you want the following: subtract amounts from 11 while remainder is positive. If this is true, here is a solution with recursive cte:
DECLARE #t TABLE ( id INT, amount INT )
INSERT INTO #t VALUES
( 1, 10 ),
( 2, 5 ),
( 3, 3 ),
( 4, 2 );
WITH cte
AS ( SELECT * , 17 - amount AS remainder
FROM #t
WHERE id = 1
UNION ALL
SELECT t.* , c.remainder - t.amount AS remainder
FROM #t t
CROSS JOIN cte c
WHERE t.id = c.id + 1 AND c.remainder > 0
)
UPDATE t
SET amount = CASE WHEN c.remainder > 0 THEN 0
ELSE -remainder
END
FROM #t t
JOIN cte c ON c.id = t.id
SELECT * FROM #t
Output:
id amount
1 0
2 0
3 1
4 2
Here I use 17 as start remainder.
If you use sql server 2012+ then you can do it like:
WITH cte
AS ( SELECT * ,
17 - SUM(amount) OVER ( ORDER BY id ) AS remainder
FROM #t
)
SELECT id ,
CASE WHEN remainder >= 0 THEN 0
WHEN remainder < 0
AND LAG(remainder) OVER ( ORDER BY id ) >= 0
THEN -remainder
ELSE amount
END
FROM cte
First you should get a cumulative sum on amount:
select
id,
amount,
sum(amount) over (order by id) running_sum
from #TEMP;
From here we should put 0 on rows before running_sum exceeds the value 11. Update the row where the running sum exceeds 11 and do nothing to rows after precedent row.
select
id,
amount
running_sum,
min(case when running_sum > 11 then id end) over () as decide
from (
select
id,
amount,
sum(amount) over (order by id) running_sum
from #TEMP
);
From here we can do the update:
merge into #TEMP t
using (
select
id,
amount
running_sum,
min(case when running_sum > 11 then id end) over () as decide
from (
select
id,
amount,
sum(amount) over (order by id) running_sum
from #TEMP
)
)a on a.id=t.id
when matched then update set
t.amount = case when a.id = a.decide then a.running_sum - 11
when a.id < a.decide then 0
else a.amount
end;
See an SQLDFIDDLE

duplicate entry in union

I have three tables:
1. Flat Discount
2. Promotion
3. weeklyorder
When i join these table and take union i got 2 row with same data but one different .. how to merge it to show only one row.
Query:
SELECT skuMaster.SKU,
(skuMaster.MinimumStock - COUNT(*)) as ReorderQuantity,
'LowInventory' as descp
FROM SKUMaster skuMaster
JOIN InventoryMaster inventoryMaster ON skuMaster.SKU = inventoryMaster.SKU
GROUP BY skuMaster.sku, skuMaster.MinimumStock, skuMaster.Name
HAVING COUNT(*) < skuMaster.MinimumStock
UNION
SELECT WeeklyOrderList.SKU,
WeeklyOrderList.Quantity as ReorderQuantity,
'NoPO' as descp
FROM WeeklyOrderList
WHERE WeeklyOrderList.POCGen = 'true'
result :
SKU ReorderQuantity descp
1 1 LowInventory
2 2 LowInventory
2 2 NoPO
6 5 LowInventory
here 2nd And 3rd are alomost same only description is different.
can we combine them and show only one row with descp as lowinventory and NOPO
SKU ReorderQuantity descp
1 1 LowInventory
2 2 LowInventory NoPo
6 5 LowInventory
same as above suppose we have table below
SKU ReorderQuantity
1 1
2 5
2 10
6 5
here output should be Max reorder quantity of same sku
Result:
SKU ReorderQuantity
1 1
2 10
6 5
;WITH CTE AS
(
SELECT skuMaster.SKU,
(skuMaster.MinimumStock - COUNT(*)) as ReorderQuantity,
'LowInventory' as descp
FROM SKUMaster skuMaster
JOIN InventoryMaster inventoryMaster ON skuMaster.SKU = inventoryMaster.SKU
GROUP BY skuMaster.sku, skuMaster.MinimumStock, skuMaster.Name
HAVING COUNT(*) < skuMaster.MinimumStock
UNION
SELECT WeeklyOrderList.SKU,
WeeklyOrderList.Quantity as ReorderQuantity,
'NoPO' as descp
FROM WeeklyOrderList
WHERE WeeklyOrderList.POCGen = 'true'
)
SELECT DISTINCT
a.SKU,
a.ReorderQuantity,
descp = STUFF((SELECT ', ' + b.descp
FROM CTE b
WHERE b.ReorderQuantity = a.ReorderQuantity
FOR XML PATH('')), 1, 2, '')
FROM CTE a
just an example for above output mentioned
DECLARE #t TABLE
(
SKU INT,
ReorderQuantity INT
)
INSERT INTO #t (SKU,ReorderQuantity)
VALUES (1,1), (2,5), (2,10), (6,5)
SELECT t.SKU,tt.Qty
FROM #t t
INNER JOIN (SELECT MAX(ReorderQuantity)as Qty, SKU
FROM #t
GROUP BY SKU) tt
ON tt.SKU = t.SKU
GROUP BY t.SKU,tt.Qty

TSQL Sweepstakes Script

I need to run a sweepstakes script to get X amount of winners from a customers table. Each customer has N participations. The table looks like this
CUSTOMER-A 5
CUSTOMER-B 8
CUSTOMER-C 1
I can always script to have CUSTOMER-A,B and C inserted 5, 8 and 1 times respectively in a temp table and then select randomly using order by newid() but would like to know if there's a more elegant way to address this.
(Update: Added final query.)
(Update2: Added single query to avoid temp table.)
Here's the hard part using a recursive CTE plus the final query that shows "place".
Code
DECLARE #cust TABLE (
CustomerID int IDENTITY,
ParticipationCt int
)
DECLARE #list TABLE (
CustomerID int,
RowNumber int
)
INSERT INTO #cust (ParticipationCt) VALUES (5)
INSERT INTO #cust (ParticipationCt) VALUES (8)
INSERT INTO #cust (ParticipationCt) VALUES (1)
INSERT INTO #cust (ParticipationCt) VALUES (3)
INSERT INTO #cust (ParticipationCt) VALUES (4)
SELECT * FROM #cust
;WITH t AS (
SELECT
lvl = 1,
CustomerID,
ParticipationCt
FROM #Cust
UNION ALL
SELECT
lvl = lvl + 1,
CustomerID,
ParticipationCt
FROM t
WHERE lvl < ParticipationCt
)
INSERT INTO #list (CustomerID, RowNumber)
SELECT
CustomerID,
ROW_NUMBER() OVER (ORDER BY NEWID())
FROM t
--<< All rows
SELECT * FROM #list ORDER BY RowNumber
--<< All customers by "place"
SELECT
CustomerID,
ROW_NUMBER() OVER (ORDER BY MIN(RowNumber)) AS Place
FROM #list
GROUP BY CustomerID
Results
CustomerID ParticipationCt
----------- ---------------
1 5
2 8
3 1
4 3
5 4
CustomerID RowNumber
----------- -----------
4 1
1 2
1 3
2 4
1 5
5 6
2 7
2 8
4 9
2 10
2 11
2 12
1 13
5 14
5 15
3 16
5 17
1 18
2 19
2 20
4 21
CustomerID Place
----------- -----
4 1
1 2
2 3
5 4
3 5
Single Query with No Temp Table
It is possible to get the answer with a single query that does not use a temp table. This works fine, but I personally like the temp table version better so you can validate the interim results.
Code (Single Query)
;WITH List AS (
SELECT
lvl = 1,
CustomerID,
ParticipationCt
FROM #Cust
UNION ALL
SELECT
lvl = lvl + 1,
CustomerID,
ParticipationCt
FROM List
WHERE lvl < ParticipationCt
),
RandomOrder AS (
SELECT
CustomerID,
ROW_NUMBER() OVER (ORDER BY NEWID()) AS RowNumber
FROM List
)
SELECT
CustomerID,
ROW_NUMBER() OVER (ORDER BY MIN(RowNumber)) AS Place
FROM RandomOrder
GROUP BY CustomerID
try this:
Select Top X CustomerId
From (Select CustomerId,
Rand(CustomerId) *
Count(*) /
(Select Count(*)
From Table) Sort
From Table
Group By CustomerId) Z
Order By Sort Desc
EDIT: abovbe assumed multiple rows per customer, one row per participation... Sorry, following assumes one row per customer, with column Participations holding number of participations for that customer.
Select Top 23 CustomerId
From ( Select CustomerId,
Participations - RAND(CustomerId) *
(Select SUM(Participations ) From customers) sort
from customers) Z
Order By sort desc