Current
Name Quantity
---------------
Stella 2
Jennifer 2
Greg 3
Requested result
Name Quantity
---------------
Stella 1
Stella 1
Jennifer 1
Jennifer 1
Greg 1
Greg 1
Greg 1
How should I do it?
declare #T table
(
Name varchar(50),
Sales int
)
insert into #T values
('Stella', '2'),
('Jennifer', '2'),
('Greg', '3')
If the maximum value in the quantity column is known to be less than 32,767, you can use Recursion to generate numbers and join the Numbers to achieve your result.
/*******************************************
Max Recursion Count in SQL Server is 32767
Limitation of 32767 Numbers!
******************************************/
;WITH Numbers (Number) AS
(
SELECT 1
UNION ALL
SELECT 1 + Number FROM Numbers WHERE Number < 100
)
SELECT m.Name,
Quantity = 1
FROM MyTable m
JOIN #numbers n ON m.Quantity <= n.Number
OPTION (MAXRECURSION 32767);
Using recursion and borrowing Michael Fredrickson's setup code:
declare #T table (
Name varchar(50),
Sales int
)
insert into #T values ('Stella', '2')
insert into #T values ('Jennifer', '2')
insert into #T values ('Greg', '3')
-- Recursive verion
;with People (Name, Sales) as
(
select Name, Sales
from #T
union all
select Name, Sales - 1
from People
where Sales - 1 > 0
)
select Name, 1 as Quantity
from People
option (maxrecursion 0) -- Recurse without limit
This seems to run faster on my box (5x faster than Michael Fredrickson's according to query plan, but with many more logical reads), not that it matters much.
You'll probably want to have a pre-populated numbers table to do this:
declare #T table (
Name varchar(50),
Sales int
)
declare #numbers table (
Number int
)
insert into #numbers values (1)
insert into #numbers values (2)
insert into #numbers values (3)
insert into #numbers values (4)
-- Etc... up to however many numbers is the max possible value for sales...
insert into #T values ('Stella', '2')
insert into #T values ('Jennifer', '2')
insert into #T values ('Greg', '3')
SELECT
t.Name,
1 AS Sales
FROM
#T t JOIN
#numbers n ON
t.Sales >= n.Number
ORDER BY t.Name
That's how you could do it, but I'm not sure on why you would want to do it.
Related
I have this table let's say Students. If there are 2 students in table and result set gives 2 rows than I want to add 5 more rows with dummy data in it no matter what it says i.e 'Dummy Record' or something.
SELECT FirstName, (SELECT COUNT(*) FROM Student) Total
FROM Student
If the output is like this from above query.
FirstName Total
Isaac Frempong 2
Erick Ortiz 2
I want the output to be like this
FirstName Total
Isaac Frempong 2
Erick Ortiz 2
Dummy Data 2
Dummy Data 2
Dummy Data 2
Dummy Data 2
Dummy Data 2
I hope it's achievable, I'm unable to figure how to apply CASE or IF statements here. Maybe somebody could help.
One method is to use a VALUES() statement to construct the rows:
SELECT FirstName, COUNT(*) OVER () as Total
FROM Student
UNION ALL
SELECT 'Dummy', (SELECT COUNT(*) FROM Student)
FROM (VALUES (1), (2), (3), (4), (5)) v(n);
EDIT:
If you always want 7 rows, use arithmetic:
SELECT FirstName, COUNT(*) OVER () as Total
FROM Student
UNION ALL
SELECT 'Dummy', s.total
FROM (VALUES (1), (2), (3), (4), (5), (6), (7)) v(n) CROSS JOIN
(SELECT COUNT(*) as total FROM Student) s
WHERE v.n <= 7 - s.total;
s
Here is a code working on a table varaibale I called #students
Please replace it by your own table
I have filled this table with some Data, and tested the different cases (records number less then 7 or more than 7, and worked as you wish !)
1) First create a variable table called #mytable which will be filled by n records
n will be 7 if the number of records in your students table is equal or less than 7
n will be the number of records in your student table if it is more than 7
2) Then make a right outer join between your student table to which you add the record number using CTE ,row_number() function.
declare #students as table(id int identity(1,1),firstname nvarchar(50))
insert into #students(firstname) values
('Ali ben Hassine'),
('Mohamed el Aabed'),
('Ali ben Hassine'),
('Mohamed el Aabed'),
('Mohamed el Aabed'),
('Tahar Harbi'),
('Hassine Ayari'),
('Ihsen Trabelsi'),
('Marwa Mostari'),
('Mourad Zmerli'),
('Hafedh Gabsi'),
('Miloud Filali');
declare #mytab as table(n int)
declare #n as int
select #n=count(distinct(firstname)) from #students
if #n<7 set #n=7
declare #i as int
set #i=1
while #i <#n+1
begin
insert into #mytab values(#i)
set #i=#i+1
end;
with cte as
(select row_number() over(partition by 1 order by firstname) r#,firstname,count(1) Total from #students group by firstname)
select isnull(cte.firstname,'Dummy') firstanme,isnull(cte.total,2) Total from cte
right outer join #mytab t2 on cte.r#=t2.n
[![enter image description here][1]][1]
Say i have a table with an integer column Id. I need to find the missing numbers in a sequence with a maximum returned amount.
If the table is empty and i'm asking for 10, it should return the numbers 1-10.
If the table has 1-5 and i'm asking for 10, it should return the numbers 6,7,8,9,10,11,12,13,14,15.
If the table has 1,2,4,6,9 and im asking for 10, it should return the numbers 3,5,7,8,10,11,12,13,14,15
How can i achive this in one single query using MS SQL?
Thanks in advance!
Try this:
If you need to get more numbers, just increase the WHERE Number<=100.
DECLARE #Tab1 TABLE (ID INT)
INSERT INTO #Tab1 VALUES(1)
INSERT INTO #Tab1 VALUES(3)
INSERT INTO #Tab1 VALUES(5)
INSERT INTO #Tab1 VALUES(7)
INSERT INTO #Tab1 VALUES(9)
;WITH CTE AS
(
SELECT 1 AS Number
UNION ALL
SELECT Number + 1 FROM CTE
WHERE Number<=100
)
SELECT TOP 5 *
FROM CTE
WHERE Number NOT IN(SELECT ID FROM #Tab1)
ORDER BY Number
OPTION (maxrecursion 0);
Existing values:
Number
1
3
5
7
9
OutPut:
Number
2
4
6
8
10
Hope this helps you.
This should work
There are also a system table with numbers
declare #T table (i int primary key);
insert into #T values (1), (2), (4), (6), (9);
declare #count int = 10;
declare #size int = (select count(*) from #T);
with cte as
( select 1 as num
union all
select num + 1
from cte
where num + 1 <= (#count + #size)
)
select top (#count) cte.num
from cte
left join #T t
on t.i = cte.num
where t.i is null
order by cte.num
option ( MaxRecursion 0 );
I'm probably over thinking this. I have a simple table with Name and Ticket Quantity columns. I want to output a row by row list of the names for each quantity purchased. See example below.
Table:
Name Quantity
-----------------------
Bob 1
Joe 2
Sally 1
Output:
Bob
Joe
Joe
Sally
How could I achieve this in TSQL?
SETUP:
DECLARE #table TABLE (
NAME VARCHAR(10),
Quantity INT
)
INSERT INTO #table
SELECT 'Bob', 1 UNION ALL
SELECT 'Joe', 2 UNION ALL
SELECT 'Sally', 1
Recursive CTE
;WITH Members (
NAME,
Quantity
)
AS (
-- Base case
SELECT NAME,
Quantity
FROM #table
UNION ALL
-- Recursive
SELECT NAME,
Members.Quantity - 1
FROM Members
WHERE Members.Quantity > 1
)
SELECT NAME
FROM Members
OPTION (MAXRECURSION 0)
ORDER BY 1
Result:
Bob
Joe
Joe
Sally
Alternatively you could (per #Martin Smith's suggestion):
DECLARE #numbers TABLE (number INT)
INSERT INTO #numbers (number)
VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10)
Finally:
SELECT NAME
FROM #table t
INNER JOIN #numbers n ON n.number <= t.Quantity
ORDER BY 1
Result:
Bob
Joe
Joe
Sally
And if you really like recursive CTE's (because they smell good), you could build your numbers table with a recursive CTE. You should be using physical tables and not variable tables as you see here - so that you don't have to build them every time.
;WITH Numbers (Value)
AS (
-- Base case
SELECT 32767 Value
UNION ALL
-- Recursive
SELECT Numbers.Value - 1
FROM Numbers
WHERE Numbers.Value > 1
)
INSERT INTO #numbers (number)
SELECT Value
FROM Numbers
OPTION (MAXRECURSION 32767)
Considering a table customerBalance with 3 columns: name, date and balance. Suppose a set of records like:
cus_name cus_date cus_balance
John 06/14/2011 1000
John 06/15/2011 500
John 06/16/2011 0
Mary 06/14/2011 3000
Mary 06/15/2011 2800
Mary 06/16/2011 0
How to create a SQL query which returns, for the date 6/16/2011 instead 0, the last non-zero value based on date (in sample, $500 for John and $2800 for Mary)?
I'm trying to do it using a subquery which uses Max function to retrieve the last date with non-zero value, but I didn't succeed. This example is quite "nonsensical", but I really need to do an operation like this in my dataset. Thanks!
Note: If you can specify the DB and version this query can be improved.
Try this:
SELECT *
FROM customers
WHERE (cus_name, cus_date)
IN
(
SELECT cus_name, MAX(cus_date)
FROM customers
WHERE cus_balance <> 0
GROUP BY cus_name
)
Update: Alternate version:
SELECT a.*
FROM customers a,
(
SELECT cus_name, MAX(cus_date)
FROM customers
WHERE cus_balance <> 0
GROUP BY cus_name
) b
WHERE a.cus_name = b.cus_name
AND a.cus_date = b.cus_date
Here it goes:
CREATE Table #temp
(
Cus_Name VARCHAR(200) NULL,
Cus_Date Char(8) NULL,
Cus_Balance INT NULL
)
INSERT INTO #temp VALUES ('John' , '20110614' ,1000 )
INSERT INTO #temp VALUES ('John' , '20110615' , 500 )
INSERT INTO #temp VALUES ('John' , '20110616' , 0 )
INSERT INTO #temp VALUES ('Mary' , '20110614' ,3000 )
INSERT INTO #temp VALUES ('Mary' , '20110615' ,2800 )
INSERT INTO #temp VALUES ('Mary' , '20110616' , 0 )
SELECT
T.Cus_Name ,
MIN(t.Cus_Balance)
FROM #temp t
WHERE t.Cus_Balance <>0
GROUP BY t.Cus_Name
DROP TABLE #temp
I have the following sample data:
Id Name Quantity
1 Red 1
2 Red 3
3 Blue 1
4 Red 1
5 Yellow 3
So for this example, there are a total of 5 Red, 1 Blue, and 3 Yellow. I am looking for a way to group them by Color, but with a maximum of 2 items per group (sorting is not important). Like so:
Name QuantityInPackage
Red 2
Red 2
Red 1
Blue 1
Yellow 2
Yellow 1
Any suggestions on how to accomplish this using T-SQL on MS-SQL 2005?
I would define a table containing sequential numbers, say 1 to 1000 and join that table (unless your database supports generating these numbers in the query like Oracle using CONNECT BY):
Table num
n
1
2
3
...
I tried the following query using Oracle (should work with TSQL too):
With summed_colors As (
Select name, Sum(quantity) quantity
From colors
Group By name
)
Select
name,
Case When n*2-1 = quantity Then 1 Else 2 End quantityInPackage
From summed_colors
Join nums On ( n*2-1 <= quantity )
Order By name, quantityInPackage Desc
and it returns
Blue 1
Red 2
Red 2
Red 1
Yellow 2
Yellow 1
You need to use a numbers table to unpivot your data to make multiple rows:
DECLARE #PackageSize AS int
SET #PackageSize = 2
DECLARE #numbers AS TABLE (Number int)
INSERT INTO #numbers
VALUES (1)
INSERT INTO #numbers
VALUES (2)
INSERT INTO #numbers
VALUES (3)
INSERT INTO #numbers
VALUES (4)
INSERT INTO #numbers
VALUES (5)
INSERT INTO #numbers
VALUES (6)
INSERT INTO #numbers
VALUES (7)
INSERT INTO #numbers
VALUES (8)
INSERT INTO #numbers
VALUES (9)
INSERT INTO #numbers
VALUES (10)
DECLARE #t AS TABLE
(
Id int
,Nm varchar(6)
,Qty int
)
INSERT INTO #t
VALUES (1, 'Red', 1)
INSERT INTO #t
VALUES (2, 'Red', 3)
INSERT INTO #t
VALUES (3, 'Blue', 1)
INSERT INTO #t
VALUES (4, 'Red', 1)
INSERT INTO #t
VALUES (5, 'Yellow', 3) ;
WITH Totals
AS (
SELECT Nm
,SUM(Qty) AS TotalQty
,SUM(Qty) / #PackageSize AS NumCompletePackages
,SUM(Qty) % #PackageSize AS PartialPackage
FROM #t
GROUP BY Nm
)
SELECT Totals.Nm
,#PackageSize AS QuantityInPackage
FROM Totals
INNER JOIN #numbers AS numbers
ON numbers.Number <= Totals.NumCompletePackages
UNION ALL
SELECT Totals.Nm
,PartialPackage AS QuantityInPackage
FROM Totals
WHERE PartialPackage <> 0
It's not grouping or modulo/division that's the hard part here, it's the fact that you need to do an aggregate (sum) and then explode the data again. There aren't actually any "Red 2" rows, you have to create them somehow.
For SQL Server 2005+, I would probably use a function do the "exploding":
CREATE FUNCTION dbo.CreateBuckets
(
#Num int,
#MaxPerGroup int
)
RETURNS TABLE
AS RETURN
WITH First_CTE AS
(
SELECT CASE
WHEN #MaxPerGroup < #Num THEN #MaxPerGroup
ELSE #Num
END AS Seed
),
Sequence_CTE AS
(
SELECT Seed AS [Current], Seed AS Total
FROM First_CTE
UNION ALL
SELECT
CASE
WHEN (Total + #MaxPerGroup) > #Num THEN (#Num - Total)
ELSE #MaxPerGroup
END,
Total + #MaxPerGroup
FROM Sequence_CTE
WHERE Total < #Num
)
SELECT [Current] AS Num
FROM Sequence_CTE
Then, in the main query, group (sum) the data first and then use the bucket function:
WITH Totals AS
(
SELECT Name, SUM(Quantity) AS Total
FROM Table
GROUP BY Name
)
SELECT Name, b.Num AS QuantityInPackage
FROM Totals
CROSS APPLY dbo.CreateBuckets(Total, 2) b
This should work for any bucket size, doesn't have to be 2 (just change the parameter).
This is very crude, but it works.
CREATE TABLE #Colors
(
Id int,
Name varchar(50),
Quantity int
)
INSERT INTO #Colors VALUES (1, 'Red', 1)
INSERT INTO #Colors VALUES (2, 'Red', 3)
INSERT INTO #Colors VALUES (3, 'Blue', 1)
INSERT INTO #Colors VALUES (4, 'Red', 1)
INSERT INTO #Colors VALUES (5, 'Yellow', 3)
INSERT INTO #Colors VALUES (6, 'Green', 2)
SELECT
Name,
SUM(Quantity) AS TotalQuantity
INTO #Summed
FROM
#Colors
GROUP BY
Name
SELECT
Name,
TotalQuantity / 2 AS RecordsWithQuantity2,
TotalQuantity % 2 AS RecordsWithQuantity1
INTO #SortOfPivot
FROM
#Summed
ORDER BY
Name
DECLARE #RowCount int
SET #RowCount = (SELECT COUNT(*) FROM #SortOfPivot)
DECLARE #Name varchar(50)
DECLARE #TwosInsertCount int
DECLARE #OnesInsertCount int
CREATE TABLE #Result (Name varchar(50), Quantity int)
WHILE #RowCount > 0
BEGIN
SET #Name = (SELECT TOP 1 Name FROM #SortOfPivot)
SET #TwosInsertCount = (SELECT TOP 1 RecordsWithQuantity2 FROM #SortOfPivot)
SET #OnesInsertCount = (SELECT TOP 1 RecordsWithQuantity1 FROM #SortOfPivot)
WHILE #TwosInsertCount > 0
BEGIN
INSERT INTO #Result (Name, Quantity) VALUES (#Name, 2)
SET #TwosInsertCount = #TwosInsertCount - 1
END
WHILE #OnesInsertCount > 0
BEGIN
INSERT INTO #Result (Name, Quantity) VALUES (#Name, 1)
SET #OnesInsertCount = #OnesInsertCount - 1
END
DELETE FROM #SortOfPivot WHERE Name = #Name
SET #RowCount = (SELECT COUNT(*) FROM #SortOfPivot)
END
SELECT * FROM #Result
DROP TABLE #Colors
DROP TABLE #Result
DROP TABLE #Summed
DROP TABLE #SortOfPivot