How to join 2 table variables which are not consists of a foreign key column.
DECLARE #InventoryIDList TABLE(ID INT)
DECLARE #ProductSupplierIDList TABLE(ID INT)
Excepted output
#InventoryList
--------------
123
456
789
111
#ProductSupplierIDList
--------------
999
888
777
666
#InventoryList ProductSupplierIDList
---------------------------------------
123 | 999
567 | 888
789 | 777
111 | 666
All are random data. I just want to combine the 2 table variable to look like above. I tried all the types of joins. But I need to have the upper mentioned output without having null values.
I tried the CROSS APPLY
SELECT *
FROM #InventoryIDList invList CROSS APPLY #ProductSupplierIDList prdList
But it gives me 5^2 number of elements as the result with duplicates.
Since the IDs are not in sequential order and can be random, I would recommend using an Identity on the table variables and joining on that:
DECLARE #InventoryIDList TABLE(JoiningID INT IDENTITY(1,1), ID INT)
DECLARE #ProductSupplierIDList TABLE(JoiningID INT IDENTITY(1,1), ID INT)
INSERT INTO #InventoryIDList
VALUES
(123),
(456),
(789),
(111)
INSERT INTO #productsupplierIDList
VALUES
(999),
(888),
(777),
(666)
SELECT i.id, p.id
FROM #inventoryIDList i
INNER JOIN #productsupplierIDList p
oN i.joiningid = p.JoiningID
I guess you need Row_Number and Full Outer Join, considering there is no relation between those 2 tables
SELECT I.ID,
P.ID
FROM (SELECT Rn = Row_number()OVER(ORDER BY ID),*
FROM #InventoryList) I
FULL JOIN (SELECT Rn = Row_number()OVER(ORDER BY ID),*
FROM #ProductSupplierIDList) p
ON I.RN = P.RN
Assuming that the JOIN criteria is the same "row number" in ascending ID order:
WITH invList AS (
SELECT *,
ROW_NUMBER() OVER (ORDER BY ID) AS RN
FROM #InventoryIDList),
prdList AS (
SELECT *,
ROW_NUMBER() OVER (ORDER BY ID) AS RN
FROM #ProductSupplierIDList)
SELECT *
FROM invList IL
JOIN prdList PL ON IL.RN = PL.RN;
Related
declare #t table
(
id int,
SomeNumt int
)
insert into #t
select 1,10
union
select 2,12
union
select 3,3
union
select 4,15
union
select 5,23
select * from #t
the above select returns me the following.
id SomeNumt
1 10
2 12
3 3
4 15
5 23
How do I get the following:
id srome CumSrome
1 10 10
2 12 22
3 3 25
4 15 40
5 23 63
select t1.id, t1.SomeNumt, SUM(t2.SomeNumt) as sum
from #t t1
inner join #t t2 on t1.id >= t2.id
group by t1.id, t1.SomeNumt
order by t1.id
SQL Fiddle example
Output
| ID | SOMENUMT | SUM |
-----------------------
| 1 | 10 | 10 |
| 2 | 12 | 22 |
| 3 | 3 | 25 |
| 4 | 15 | 40 |
| 5 | 23 | 63 |
Edit: this is a generalized solution that will work across most db platforms. When there is a better solution available for your specific platform (e.g., gareth's), use it!
The latest version of SQL Server (2012) permits the following.
SELECT
RowID,
Col1,
SUM(Col1) OVER(ORDER BY RowId ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS Col2
FROM tablehh
ORDER BY RowId
or
SELECT
GroupID,
RowID,
Col1,
SUM(Col1) OVER(PARTITION BY GroupID ORDER BY RowId ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS Col2
FROM tablehh
ORDER BY RowId
This is even faster. Partitioned version completes in 34 seconds over 5 million rows for me.
Thanks to Peso, who commented on the SQL Team thread referred to in another answer.
For SQL Server 2012 onwards it could be easy:
SELECT id, SomeNumt, sum(SomeNumt) OVER (ORDER BY id) as CumSrome FROM #t
because ORDER BY clause for SUM by default means RANGE UNBOUNDED PRECEDING AND CURRENT ROW for window frame ("General Remarks" at https://msdn.microsoft.com/en-us/library/ms189461.aspx)
Let's first create a table with dummy data:
Create Table CUMULATIVESUM (id tinyint , SomeValue tinyint)
Now let's insert some data into the table;
Insert Into CUMULATIVESUM
Select 1, 10 union
Select 2, 2 union
Select 3, 6 union
Select 4, 10
Here I am joining same table (self joining)
Select c1.ID, c1.SomeValue, c2.SomeValue
From CumulativeSum c1, CumulativeSum c2
Where c1.id >= c2.ID
Order By c1.id Asc
Result:
ID SomeValue SomeValue
-------------------------
1 10 10
2 2 10
2 2 2
3 6 10
3 6 2
3 6 6
4 10 10
4 10 2
4 10 6
4 10 10
Here we go now just sum the Somevalue of t2 and we`ll get the answer:
Select c1.ID, c1.SomeValue, Sum(c2.SomeValue) CumulativeSumValue
From CumulativeSum c1, CumulativeSum c2
Where c1.id >= c2.ID
Group By c1.ID, c1.SomeValue
Order By c1.id Asc
For SQL Server 2012 and above (much better performance):
Select
c1.ID, c1.SomeValue,
Sum (SomeValue) Over (Order By c1.ID )
From CumulativeSum c1
Order By c1.id Asc
Desired result:
ID SomeValue CumlativeSumValue
---------------------------------
1 10 10
2 2 12
3 6 18
4 10 28
Drop Table CumulativeSum
A CTE version, just for fun:
;
WITH abcd
AS ( SELECT id
,SomeNumt
,SomeNumt AS MySum
FROM #t
WHERE id = 1
UNION ALL
SELECT t.id
,t.SomeNumt
,t.SomeNumt + a.MySum AS MySum
FROM #t AS t
JOIN abcd AS a ON a.id = t.id - 1
)
SELECT * FROM abcd
OPTION ( MAXRECURSION 1000 ) -- limit recursion here, or 0 for no limit.
Returns:
id SomeNumt MySum
----------- ----------- -----------
1 10 10
2 12 22
3 3 25
4 15 40
5 23 63
Late answer but showing one more possibility...
Cumulative Sum generation can be more optimized with the CROSS APPLY logic.
Works better than the INNER JOIN & OVER Clause when analyzed the actual query plan ...
/* Create table & populate data */
IF OBJECT_ID('tempdb..#TMP') IS NOT NULL
DROP TABLE #TMP
SELECT * INTO #TMP
FROM (
SELECT 1 AS id
UNION
SELECT 2 AS id
UNION
SELECT 3 AS id
UNION
SELECT 4 AS id
UNION
SELECT 5 AS id
) Tab
/* Using CROSS APPLY
Query cost relative to the batch 17%
*/
SELECT T1.id,
T2.CumSum
FROM #TMP T1
CROSS APPLY (
SELECT SUM(T2.id) AS CumSum
FROM #TMP T2
WHERE T1.id >= T2.id
) T2
/* Using INNER JOIN
Query cost relative to the batch 46%
*/
SELECT T1.id,
SUM(T2.id) CumSum
FROM #TMP T1
INNER JOIN #TMP T2
ON T1.id > = T2.id
GROUP BY T1.id
/* Using OVER clause
Query cost relative to the batch 37%
*/
SELECT T1.id,
SUM(T1.id) OVER( PARTITION BY id)
FROM #TMP T1
Output:-
id CumSum
------- -------
1 1
2 3
3 6
4 10
5 15
Select
*,
(Select Sum(SOMENUMT)
From #t S
Where S.id <= M.id)
From #t M
You can use this simple query for progressive calculation :
select
id
,SomeNumt
,sum(SomeNumt) over(order by id ROWS between UNBOUNDED PRECEDING and CURRENT ROW) as CumSrome
from #t
There is a much faster CTE implementation available in this excellent post:
http://weblogs.sqlteam.com/mladenp/archive/2009/07/28/SQL-Server-2005-Fast-Running-Totals.aspx
The problem in this thread can be expressed like this:
DECLARE #RT INT
SELECT #RT = 0
;
WITH abcd
AS ( SELECT TOP 100 percent
id
,SomeNumt
,MySum
order by id
)
update abcd
set #RT = MySum = #RT + SomeNumt
output inserted.*
For Ex: IF you have a table with two columns one is ID and second is number and wants to find out the cumulative sum.
SELECT ID,Number,SUM(Number)OVER(ORDER BY ID) FROM T
Once the table is created -
select
A.id, A.SomeNumt, SUM(B.SomeNumt) as sum
from #t A, #t B where A.id >= B.id
group by A.id, A.SomeNumt
order by A.id
The SQL solution wich combines "ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW" and "SUM" did exactly what i wanted to achieve.
Thank you so much!
If it can help anyone, here was my case. I wanted to cumulate +1 in a column whenever a maker is found as "Some Maker" (example). If not, no increment but show previous increment result.
So this piece of SQL:
SUM( CASE [rmaker] WHEN 'Some Maker' THEN 1 ELSE 0 END)
OVER
(PARTITION BY UserID ORDER BY UserID,[rrank] ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS Cumul_CNT
Allowed me to get something like this:
User 1 Rank1 MakerA 0
User 1 Rank2 MakerB 0
User 1 Rank3 Some Maker 1
User 1 Rank4 Some Maker 2
User 1 Rank5 MakerC 2
User 1 Rank6 Some Maker 3
User 2 Rank1 MakerA 0
User 2 Rank2 SomeMaker 1
Explanation of above: It starts the count of "some maker" with 0, Some Maker is found and we do +1. For User 1, MakerC is found so we dont do +1 but instead vertical count of Some Maker is stuck to 2 until next row.
Partitioning is by User so when we change user, cumulative count is back to zero.
I am at work, I dont want any merit on this answer, just say thank you and show my example in case someone is in the same situation. I was trying to combine SUM and PARTITION but the amazing syntax "ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW" completed the task.
Thanks!
Groaker
Above (Pre-SQL12) we see examples like this:-
SELECT
T1.id, SUM(T2.id) AS CumSum
FROM
#TMP T1
JOIN #TMP T2 ON T2.id < = T1.id
GROUP BY
T1.id
More efficient...
SELECT
T1.id, SUM(T2.id) + T1.id AS CumSum
FROM
#TMP T1
JOIN #TMP T2 ON T2.id < T1.id
GROUP BY
T1.id
Try this
select
t.id,
t.SomeNumt,
sum(t.SomeNumt) Over (Order by t.id asc Rows Between Unbounded Preceding and Current Row) as cum
from
#t t
group by
t.id,
t.SomeNumt
order by
t.id asc;
Try this:
CREATE TABLE #t(
[name] varchar NULL,
[val] [int] NULL,
[ID] [int] NULL
) ON [PRIMARY]
insert into #t (id,name,val) values
(1,'A',10), (2,'B',20), (3,'C',30)
select t1.id, t1.val, SUM(t2.val) as cumSum
from #t t1 inner join #t t2 on t1.id >= t2.id
group by t1.id, t1.val order by t1.id
Without using any type of JOIN cumulative salary for a person fetch by using follow query:
SELECT * , (
SELECT SUM( salary )
FROM `abc` AS table1
WHERE table1.ID <= `abc`.ID
AND table1.name = `abc`.Name
) AS cum
FROM `abc`
ORDER BY Name
I have two tables, table1 has 2 columns as
id name
1 Amal
2 Varun
3 Sari
table2 has 3 columns as
id Subject marks
1 Maths 80
1 Malayalam 75
1 History 45
2 Maths 90
2 Malayalam 85
2 History 50
3 Maths 88
3 Malayalam 75
3 History 80
My question is to find the names who has the maximum mark for each subject (Subject wisw topper) the resultant table have to includes the fields name subject and marks
I tested with the following query
SELECT
table1.Student_Name, (table2.subject), max(table2.Marks_obt)
FROM
table2
INNER JOIN
table1 ON table2.stud_id = table1.Student_ID
GROUP BY
[Student_Name], table2.Subject
HAVING
MAX(Marks_obt) IN (SELECT MAX(Marks_obt) AS total_marks
FROM table2
GROUP BY subject)
In SQL Server 2008, but I got the result as
name subject
Sari History 80
Varun Malayalam 85
Amal Maths 80
Varun Maths 90
how I get the topper of three subject with these manner?
You can use ROW_NUMBER() :
SELECT s.subject,s.name,s.marks
FROM(
SELECT t1.*,t2.subject,t2.marks,
ROW_NUMBER() OVER(PARTITION BY t2.subject ORDER BY t2.marks DESC) as rnk
FROM Table1
JOIN Table2
ON table2.stud_id = table1.Student_ID) s
WHERE s.rnk = 1
Use Rank You will not miss any people for example like two people will get same highest marks in same subject. And if you want query to find 2nd highest marks or 3rd highest use Dense_Rank() function even dense_rank() also works for finding 1st highest. For More cilck here
SELECT NAME,
SUBJECT,
MARKS
FROM (SELECT NAME,
SUBJECT,
MARKS,
rank()
OVER(
PARTITION BY [SUBJECT]
ORDER BY MARKS DESC) RNO
FROM #TABLE1 T
JOIN #TABLE2 T2
ON T.ID = T2.ID) A
WHERE RNO = 1
CREATE TABLE #table1
(Student_ID INT,
Student_Name VARCHAR(20))
INSERT INTO #table1
SELECT 1,'Amal'
UNION
SELECT 2,'Varun'
UNION
SELECT 3,'Sari'
CREATE TABLE #table2
(
stud_id INT,
[subject] VARCHAR(20),
Marks_obt INT
)
INSERT INTO #table2
SELECT 1,'Maths',80
UNION
SELECT 1,'Malayalam',75
UNION
SELECT 1,'History',45
UNION
SELECT 2,'Maths',90
UNION
SELECT 2,'Malayalam',85
UNION
SELECT 2,'History',80
UNION
SELECT 3,'Maths',88
UNION
SELECT 3,'Malayalam',75
UNION
SELECT 3,'History',80
/*Table 1*/
SELECT * FROM #table1
/*Table 2*/
SELECT * FROM #table2
/*Top Mark*/
SELECT [subject],
Student_Name,
Marks_obt
FROM(SELECT Student_Name,
[subject],
Marks_obt,
RANK()
OVER(
PARTITION BY [subject]
ORDER BY Marks_obt DESC) RowNum
FROM #table1 T1
JOIN #table2 T2
ON T1.Student_ID= T2.stud_id) AS data
WHERE data.RowNum = 1
DROP TABLE #table1,#table2
you can use a cross apply too like this
with maxi as (
select Subject, max(marks) maximark from table2
group by Subject
)
select * from maxi f1
cross apply
(
select top 1 f2.name from table1 f2 inner join table2 f3 on f2.id=f3.id
where f1.maximark=f3.marks and f1.subject=f3.subject
) f3
if multiple users are possible for a maxi mark, remove "top 1"
other solution with imbication:
with maxi as (
select Subject, max(marks) maximark from table2
group by Subject
)
select (select top 1 f2.name from table1 f2 inner join table2 f3 on f2.id=f3.id where f1.maximark=f3.marks and f1.Subject=f3.Subject) as Name, f1.*
from maxi f1
I have a select statement in which I am using in clause.
Here is my table : MyTable
Id SKU
1 112
2 223
3 445
4 456
5 678
If I write:
SELECT Id
FROM MyTable
WHERE SKU IN (112,223,445, 456, 678)
I an not getting result as
1
2
3
4
5
Is there any way to get select result based on items order in the in clause.?
For your case ORDER BY id will be sufficient.
SELECT Id
FROM MyTable
WHERE SKU IN (112,223,445, 456, 678)
ORDER BY id
For general approach you could use JOIN with derived table like:
Demo
SELECT m.Id
FROM MyTable m
JOIN (VALUES (1, 112) ,(2,223) ,(3,445), (4,456), (5,678)) AS t(num, SKU)
ON m.SKU = t.SKU
ORDER BY t.num
If you use SQL Server 2008 you can use UNION ALL:
Demo2
;WITH cte AS
(
SELECT 112 AS SKU, 1 AS orderNum
UNION ALL
SELECT 223 AS SKU, 2 AS orderNum
UNION ALL
SELECT 445 AS SKU, 3 AS orderNum
UNION ALL
SELECT 456 AS SKU, 4 AS orderNum
UNION ALL
SELECT 678 AS SKU, 5 AS orderNum
)
SELECT m.Id
FROM #MyTable m
JOIN cte c
ON m.SKU = c.SKU
ORDER BY c.orderNum;
General approach that does not force you to create custom query you could use temp table with IDENTITY column like:
Demo3
CREATE TABLE #mySKU( orderNum INT IDENTITY(1,1), SKU INT);
INSERT INTO #mySKU
VALUES (112),(223),(445), (456), (678);
SELECT m.Id
FROM #MyTable m
JOIN #mySKU c
ON m.SKU = c.SKU
ORDER BY c.orderNum;
"Is there any way to get select result based on items order in the in clause?"
For this particular question the answer is no.
I have one problem that i can't find nice solution for.
I have relational table Groups_Members with columns GroupId and MemberId.
I have a stored procedure that creates a new group and receives an array of memberId as parameter (user defined type). What I want is to make sure that there is not a group with exactly the same members in the database already.
I'm trying to figure out how EXCEPT operator might help me but I can't. I need condition that would return the group that has exactly the same set of members as those in my memberId parameter (or null or 0 if such group doesn't exist).
Any help would be highly appreciated!
thanks!
Table Groups_Members
GroupId|MemberId
1 | 1
1 | 2
1 | 3
2 | 1
2 | 2
2 | 4
3 | 1
3 | 3
3 | 4
Declare #Members table(id int)
insert into #Members
values(1), (3), (4)
Declare #MemberCount int
Select #MemberCount = count(id) From #Members
Select GroupId from
(Select distinct groupId, memberid from Groups_Members) gm
Inner Join #Members On MemberId = id
group by GroupId
Having COUNT(MemberId) = #MemberCount
Result would be 3
Description can be provided on demand.
Declare #Members table(id int)
insert into #Members
values(1), (3), (4)
Declare #MemberCount int
Select #MemberCount = count(id) From #Members
--Select GroupId from
--(Select distinct groupId, memberid from Groups_Members) gm
--Inner Join #Members On MemberId = id
--group by GroupId
--Having COUNT(MemberId) = #MemberCount
When values(1), (3), (4) (it works correctly).
But when values(1), (2) (it does not work correctly).
Nice try but it does not provide the exact matching with group members.
Try this for better solution
Select Gm.GroupId from
(Select distinct GroupId, memberid from Groups_Members) gm
Inner Join #Members On MemberId = id
Inner join (Select COUNT(MemberId) as Totalmember,GroupId
from Groups_Members group by GroupId) tgm on tgm.GroupId = gm.GroupId
where Totalmember = #MemberCount
group by gm.GroupId
Having COUNT(MemberId) = #MemberCount
Description can be provided on demand.
MSSQL
Table looks like so
ID 1 | 2 | 3 | 4 | 5
AA1 1 | 1 | 1 | 2 | 1
any clues on how I could make a query to return
ID | MaxNo
AA1 | 4
, usign the above table example? I know I could write a case blah when statement, but I have a feeling there's a much simpler way of doing this
You can use UNPIVOT to get these comparable items, correctly1, into the same column, and then use ROW_NUMBER() to find the highest valued row2:
declare #t table (ID char(3) not null,[1] int not null,[2] int not null,
[3] int not null,[4] int not null,[5] int not null)
insert into #t (ID,[1],[2],[3],[4],[5]) values
('AA1',1,1,1,2,1)
;With Unpivoted as (
select *,ROW_NUMBER() OVER (ORDER BY Value desc) rn
from #t t UNPIVOT (Value FOR Col in ([1],[2],[3],[4],[5])) u
)
select * from Unpivoted where rn = 1
Result:
ID Value Col rn
---- ----------- ------------------------- --------------------
AA1 2 4 1
1 If you have data from the same "domain" appearing in multiple columns in the same table (such that it even makes sense to compare such values), it's usually a sign of attribute splitting, where part of your data has, incorrectly, been used to form part of a column name.
2 In your question, you say "per row", and yet you've only given a one row sample. If we assume that ID values are unique for each row, and you want to find the maximum separately for each ID, you'd write the ROW_NUMBER() as ROW_NUMBER() OVER (PARTITION BY ID ORDER BY Value desc) rn, to get (I hope) the result you're looking for.
You can use a cross apply where you do max() over the columns for one row.
select T1.ID,
T2.Value
from YourTable as T1
cross apply
(
select max(T.Value) as Value
from (values (T1.[1]),
(T1.[2]),
(T1.[3]),
(T1.[4]),
(T1.[5])) as T(Value)
) as T2
If you are on SQL Server 2005 you can use union all in the derived table instead of values().
select T1.ID,
T2.Value
from YourTable as T1
cross apply
(
select max(T.Value) as Value
from (select T1.[1] union all
select T1.[2] union all
select T1.[3] union all
select T1.[4] union all
select T1.[5]) as T(Value)
) as T2
SQL Fiddle