For Each loop in Stored Procedure - sql-server

I have a stored procedure with three parameters:
Parameter 1 - has 200 data
Parameter 2 - has 6 data
Parameter 3 - 150 data
I want to dump all possible combinations of the result into a table. Any ideas please?
I want to do something like the following
For each Parameter 1
For each parameter 2
For each parameter 3

Assuming that the parameters are table valued parmeters, and by "data" you mean rows - to get all possible combinations you use a cross join, just like with regular tables:
-- It's better practice to specify the columns but I don't know what your columns are...
SELECT *
FROM #TVP1
CROSS JOIN #TVP2
CROSS JOIN #TVP3
The cross join will return every possible combination between the rows of both tables - here is another example to help you visualize this:
DECLARE #T as table
(
Col int
);
INSERT INTO #T (Col) VALUES (0), (1);
SELECT t1.Col As C1,
t2.Col As C2,
t3.Col As C3
FROM #T t1
CROSS JOIN #T t2
CROSS JOIN #T t3
ORDER BY C1, C2, C3
C1 C2 C3
0 0 0
0 0 1
0 1 0
0 1 1
1 0 0
1 0 1
1 1 0
1 1 1

Related

SQL Server, subset a table and create new column based on condition

If I have a table in SQL as:
id
code
1
6
1
8
1
4
2
3
2
7
2
4
3
7
3
6
3
7
What I need to do, logically is:
Get the top row of each group when grouped by id, ordered by code
Create a new column to show if the code column contained a 7 anywhere, within the group
Desired result:
id
code
c7
1
4
N
2
3
Y
3
6
Y
I think it needs a "CASE WHEN" statement in the SELECT, but have not worked it out. What query can I execute to get this?
Seems like you can use a MIN and a conditional aggregate:
SELECT id,
MIN(Code) AS Code,
CASE WHEN COUNT(CASE code WHEN 7 THEN 1 END) > 0 THEN 'Y' ELSE 'N' END AS C7
FROM dbo.YourTable
GROUP BY id;
There is probably a better way to do this, but what comes to mind is that first you have to partition the table to get the top one based on whatever that criteria is, then join back against itself to find the ones with the 7
declare #table1 table (id int not null, code int not null)
insert into #table1 (id, code)
values
(1,6),
(1,8),
(1,4),
(2,3),
(2,7),
(2,4),
(3,7),
(3,6),
(3,7)
select id, code, c7
from (
select t.id ,t.code
,(CASE WHEN c.id is null then 'N' else 'Y' END) as c7
,ROW_NUMBER() OVER (PARTITION BY t.id order by t.code) AS p
from #table1 t
left outer join (
select id, code, 'Y' as c7
from #table1
where code = 7) c on c.id = t.id
) sorted
where sorted.p = 1

Create a view using SQL Server with repeating rows and new column

I have a table with the following columns.
EVAL_ID | GGRP_ID | GOAL_ID
1 1 1
2 2 1
2 2 2
3 1 3
I want to create a view with another columns called GOAL_VERSION which has values from 1 to 3. So that each row from the above table should be duplicated 5 times for different GOAL_VERSION numbers. The out put should be like this.
EVAL_ID | GGRP_ID | GOAL_ID |GOAL_VERSION
1 1 1 1
1 1 1 2
1 1 1 3
1 1 1 4
1 1 1 5
2 2 1 1
2 2 1 2
2 2 1 3
2 2 1 4
2 2 1 5
How can I do that. Help me. Thank you.
Is it this you are looking for?
DECLARE #tbl TABLE(EVAL_ID INT,GGRP_ID INT,GOAL_ID INT);
INSERT INTO #tbl VALUES
(1,1,1)
,(2,2,1)
,(2,2,2)
,(3,1,3);
SELECT tbl.*
,x.Nr
FROM #tbl AS tbl
CROSS JOIN (VALUES(1),(2),(3),(4),(5)) AS x(Nr)
EDIT: Varying count of repetition
DECLARE #tbl TABLE(EVAL_ID INT,GGRP_ID INT,GOAL_ID INT);
INSERT INTO #tbl VALUES
(1,1,1)
,(2,2,1)
,(2,2,2)
,(3,1,3);
DECLARE #tblCountOfRep TABLE(CountOfRep INT);
INSERT INTO #tblCountOfRep VALUES(3);
SELECT tbl.*
,y.Nr
FROM #tbl AS tbl
CROSS JOIN (SELECT TOP (SELECT CountOfRep FROM #tblCountOfRep) * FROM(VALUES(1),(2),(3),(4),(5) /*add the max count here*/) AS x(Nr)) AS y
In this case I'd prefer I numbers table...
Take a look at CROSS JOIN. If you make a table that's got one column with the 5 rows you want you can just CROSS JOIN it to get the result you're after.
You can achieve this using a CTE and CROSS APPLY:
;WITH CTE AS
(
SELECT 1 AS GOAL_VERSION
UNION
SELECT 2
UNION
SELECT 3
UNION
SELECT 4
UNION
SELECT 5
)
SELECT * FROM <your table>
CROSS APPLY CTE
use "with" (cte) with rank clause for creating view.
If you have a numbers table in SQL database, you can cross join your table with the numbers table for numbers between 1 and 5
Here is my SQL solution for your requirement
select
goals.*,
n.i as GOAL_VERSION
from goals, dbo.NumbersTable(1,5,1) n
And here is the modified version with "cross join" as suggested in the comments
select
goals.*,
n.i as GOAL_VERSION
from goals
cross join dbo.NumbersTable(1,5,1) n
You can realize, I used a SQL table-valued function for SQL numbers table
Please create that SQL function using the source codes given in the referred tutorial
I hope it helps,

Aggregating data from multiple columns (similar to a PIVOT table)

I have a table that has the following data (shortened for this example):
C1 C2 C3 C4
=========================
1 0 1 1 0
2 1 1 0 1
3 1 0 1 1
4 1 1 1 1
5 0 0 1 1
6 0 0 0 1
I want to create a create a query that gives me the following result:
C3 C4
=============
C1 2 3
C2 2 2
That is, the combination of those four columns when:
C1 = 1 & C3 = 1
C1 = 1 & C4 = 1
C2 = 1 & C3 = 1
C2 = 1 & C4 = 1
I've been able to get make some progress, but not ultimately what I want. I've managed to get this far: http://sqlfiddle.com/#!3/7d3d4/2/0
but I can't figure out how to get my query to format like my desired output above. I was going to try to use a PIVOT table, but quickly abandoned that idea because it seemed to get extremely convoluted once I needed to add a 2nd PIVOT statement (and I'd end up having to add more as my example above is a really basic example of the number of columns I need to perform this on).
What am I missing?
Thank you.
try this:
SELECT 'C1',SUM(CASE WHEN C1=1 AND C3=1 THEN 1 END)AS C3,
SUM(CASE WHEN C1=1 AND C4=1 THEN 1 END)AS C4
FROM T1
UNION ALL
SELECT 'C2',SUM(CASE WHEN C2=1 AND C3=1 THEN 1 END)AS C3,
SUM(CASE WHEN C2=1 AND C4=1 THEN 1 END)AS C4
FROM T1
SQL Fiddle Demo
I think this gives you the result set as asked
SELECT COL1, C3, C4
FROM (
SELECT SUBSTRING(col,1,2) col1, SUBSTRING(col,3,2) col2, value
FROM (
SELECT SUM(c1*c3) as c1c3, SUM(c1*c4) as c1c4,
SUM(c2*c3) as c2c3, SUM(c2*c4) as c2c4
FROM t1
) t
UNPIVOT (value FOR col IN (c1c3, c1c4, c2c3, c2c4)) AS u
) data
PIVOT (SUM(value) FOR col2 IN ([c3], [c4])) pvt
I started writing a query that gives the totals, the resultset looks like this
c1c3 c1c4 c2c3 c2c4
2 3 2 2
At this moment you could probably just use this query and will get the result as wanted
SELECT 'c1' AS col, c1c3 AS C3, c1c4 AS C4
UNION ALL
SELECT 'c2' AS col, c2c3 AS C3, c2c4 AS C4
The first solution is just an example of how to UNPIVOT and then PIVOT again to order the columns in the way you want

Reporting on data when data is missing (ie. how to report zero activities for a customer on a given week)

I want to create a report which aggregates the number of activities per customer per week.
If there has been no activites on that customer for a given week, 0 should be displayed (i.e week 3 and 4 in the sample below)
CUSTOMER | #ACTIVITIES | WEEKNUMBER
A | 4 | 1
A | 2 | 2
A | 0 | 3
A | 0 | 4
A | 1 | 5
B ...
C ...
The problem is that if there are no activities there is no data to report on and therefor week 3 and 4 in the sample below is not in the report.
What is the "best" way to solve this?
Try this:
DECLARE #YourTable table (CUSTOMER char(1), ACTIVITIES int, WEEKNUMBER int)
INSERT #YourTable VALUES ('A' , 4 , 1)
INSERT #YourTable VALUES ('A' , 2 , 2)
INSERT #YourTable VALUES ('A' , 0 , 3)
INSERT #YourTable VALUES ('A' , 0 , 4)
INSERT #YourTable VALUES ('A' , 1 , 5)
INSERT #YourTable VALUES ('B' , 5 , 3)
INSERT #YourTable VALUES ('C' , 2 , 4)
DECLARE #StartNumber int
,#EndNumber int
SELECT #StartNumber=1
,#EndNumber=5
;WITH AllNumbers AS
(
SELECT #StartNumber AS Number
UNION ALL
SELECT Number+1
FROM AllNumbers
WHERE Number<#EndNumber
)
, AllCustomers AS
(
SELECT DISTINCT CUSTOMER FROM #YourTable
)
SELECT
n.Number AS WEEKNUMBER, c.CUSTOMER, CASE WHEN y.Customer IS NULL THEN 0 ELSE y.ACTIVITIES END AS ACTIVITIES
FROM AllNumbers n
CROSS JOIN AllCustomers c
LEFT OUTER JOIN #YourTable y ON n.Number=y.WEEKNUMBER AND c.CUSTOMER=y.CUSTOMER
--OPTION (MAXRECURSION 500)
OUTPUT:
WEEKNUMBER CUSTOMER ACTIVITIES
----------- -------- -----------
1 A 4
1 B 0
1 C 0
2 A 2
2 B 0
2 C 0
3 A 0
3 B 5
3 C 0
4 A 0
4 B 0
4 C 2
5 A 1
5 B 0
5 C 0
(15 row(s) affected)
I use a CTE to build a Numbers table, but you could build a permanent one look at this question: What is the best way to create and populate a numbers table?. You could Write the Query without a CTE (same results as above):
SELECT
n.Number AS WEEKNUMBER, c.CUSTOMER, CASE WHEN y.Customer IS NULL THEN 0 ELSE y.ACTIVITIES END AS ACTIVITIES
FROM Numbers n
CROSS JOIN (SELECT DISTINCT
CUSTOMER
FROM #YourTable
) c
LEFT OUTER JOIN #YourTable y ON n.Number=y.WEEKNUMBER AND c.CUSTOMER=y.CUSTOMER
WHERE n.Number>=1 AND n.Number<=5
ORDER BY n.Number,c.CUSTOMER
Keep a table of time periods separately, and then outer left join the activities to it.
Like:
select *
from ReportingPeriod as p
left join Activities as a on a.ReportingPeriodId = p.ReportingPeriodId;

Which SQL operation will give me the product of two tuples?

Refresh my memory. I can't recall how to join tuples (a,b) and (c) to produce (a,b)*(c). For example:
n
---
0
1
2
And
site_id value
----------- -------------
1 a
1 b
2 c
I'd like to end up with:
site_id value n
----------- -------------- --
1 a 0
1 a 1
1 a 2
1 b 0
1 b 1
1 b 2
2 c 0
2 c 1
2 c 2
How can I achieve this?
That's called a CROSS JOIN, also known as the Cartesian product.
SELECT *
FROM Table1
CROSS JOIN Table2
You can also do it without the JOIN keyword just by using a comma:
SELECT * FROM Table1, Table2
Here's full test code you can use to verify that it works:
CREATE TABLE Table1 (n int NOT NULL);
INSERT INTO Table1 (n) VALUES
(0),
(1),
(2);
CREATE TABLE Table2 (site_id int NOT NULL, value nvarchar(100) NOT NULL);
INSERT INTO Table2 (site_id, value) VALUES
(1, 'a'),
(1, 'b'),
(2, 'c');
SELECT Table2.site_id, Table2.value, Table1.n FROM Table1, Table2
Results:
site_id value n
1 a 0
1 a 1
1 a 2
1 b 0
1 b 1
1 b 2
2 c 0
2 c 1
2 c 2
Do a CROSS JOIN
You can try
Select *
FROM table1, table2

Resources