How to get running total by ranges? - sql-server

How to get a running total based on range:
name, Level, value, runningtotal;
A 5 5;
B 1 3 10;
B 2 2 10;
C 1 11;

I can't tell what column you are doing your running total on. But in 2008 the best way of doing a running total is a self join like this:
CREATE TABLE #Test(
ID INT NOT NULL PRIMARY KEY,
AValue INT NOT NULL)
INSERT INTO #Test VALUES (1,4), (2,2), (3,18)
SELECT T1.ID, T1.AValue, SUM(T2.AValue) RunningTotal
FROM #Test T1
JOIN #Test T2 ON T1.ID >= T2.ID
GROUP BY T1.ID, T1.AValue
DROP TABLE #Test

with cte(name,value) as
(
select name,sum(value) as val from A group by name
)
,
cte2 as
(
SELECT c.name,SUM(b.value) as val
FROM cte c inner join
cte b
on b.name <= c.name
GROUP BY c.name
)
select c.name,c.value,b.val from A c inner join cte2 b on c.name=b.name
order by c.name

Related

Find all skipped numbers per group

I have this column in my table and I want to get all skipped transactions (which is a varchar)
SA1
SA3
SA50
SA999
I'm trying to get
SA2
SA4 to SA49
SA51 to SA998
EDIT: I've checked the actual data, found out that it's non-trailing zeroes.
I've done this with PHP. Was wondering if is there a way within SQL to do it?
This might sound dumb, but what I'm trying is to plot it like this (this returns null values)
WITH cte (id) AS (
SELECT ROW_NUMBER() OVER (ORDER BY s1.[object_id])
FROM sys.all_columns AS s1
CROSS JOIN sys.all_columns AS s2
),
table1 AS (
SELECT sNumber as id1 FROM Sales WHERE sNumber LIKE '%SI_'
),
table2 AS (
SELECT sNumber as id1 FROM Sales WHERE sNumber LIKE '%SI__'
),
table3 AS (
SELECT sNumber as id1 FROM Sales WHERE sNumber LIKE '%SI___'
)
SELECT
'SA' + RIGHT('' + CAST(t1.id AS VARCHAR(2)), 1) AS id_missing
FROM cte t1
LEFT JOIN table1 t2
ON t1.id = CAST(RIGHT(t2.id1, 1) AS INT)
WHERE
t1.id < (SELECT MAX(CAST(RIGHT(id1, 1) AS INT)) FROM yourTable1) AND
t2.id1 IS NULL
UNION ALL
SELECT
'SA' + RIGHT('' + CAST(t1.id AS VARCHAR(2)), 2) AS id_missing
FROM cte t1
LEFT JOIN table2 t2
ON t1.id = CAST(RIGHT(t2.id2, 2) AS INT)
WHERE
t1.id < (SELECT MAX(CAST(RIGHT(id2, 2) AS INT)) FROM yourTable2) AND
t2.id2 IS NULL
UNION ALL
SELECT
'SA' + RIGHT('' + CAST(t1.id AS VARCHAR(3)), 3) AS id_missing
FROM cte t1
LEFT JOIN table3 t2
ON t1.id = CAST(RIGHT(t2.id3, 3) AS INT)
WHERE
t1.id < (SELECT MAX(CAST(RIGHT(id3, 3) AS INT)) FROM yourTable3) AND
t2.id3 IS NULL
You may use a calendar table approach with a left join here:
WITH cte (id) AS (
SELECT ROW_NUMBER() OVER (ORDER BY s1.[object_id])
FROM sys.all_columns AS s1
CROSS JOIN sys.all_columns AS s2
)
SELECT
'SA' + RIGHT('000' + CAST(t1.id AS VARCHAR(3)), 3) AS id_missing
FROM cte t1
LEFT JOIN yourTable t2
ON t1.id = CAST(RIGHT(t2.id, 3) AS INT)
WHERE
t1.id < (SELECT MAX(CAST(RIGHT(id, 3) AS INT)) FROM yourTable) AND
t2.id IS NULL;
Demo
The idea is to generate a sequence of numbers covering the possible up to 1000 values which might appear in your current table as SAxxx. Then, we left join this calendar table to your current table, on the condition that the numeric portion of the id does not match. All such non matching SAxxx values are then retained in the result set.

Count the match values and display in matrix form

I have the following tables with some sample data:
Table: TblTestDB
CREATE TABLE TblTestDB (id int,name varchar(100));
INSERT INTO TblTestDB VALUES(1,'Sam'),(2,'Jack'),
(3,'Rock'),(4,'Don'),(5,'Tam');
Table: TblDB1
CREATE TABLE TblDB1 (id int,name varchar(100));
INSERT INTO TblDB1 VALUES(1,'Sam'),(2,'Jack'),
(3,'Rock');
Table: TblDB2
CREATE TABLE TblDB2 (id int,name varchar(100));
INSERT INTO TblDB2 VALUES(1,'Jazz'),(2,'Dsouze'),
(3,'Rock'),(4,'Jack');
Table: TblDB3
CREATE TABLE TblDB3 (id int,name varchar(100));
INSERT INTO TblDB3 VALUES(1,'Sam'),(2,'Jazz'),
(3,'Rock');
I want to show the result in the form of:
TableName Name ID
------------------------------
TblDB1 3 3
TblDB2 2 4
TblDB3 2 3
Explaination about result set: I want to show count of column values which are matching between table TblTestDB and others(TblDB1,TblDB2,TblDB3).
Tried with the following query:
Query:
SELECT DB,MAX(Name) AS Name, MAX(ID) AS ID
FROM
(
SELECT 'TblDB1' AS DB,COUNT(a.Name) AS Name,0 AS ID
FROM TblTestDB a
INNER JOIN TblDB1 b ON a.Name = b.Name
UNION
SELECT 'TblDB2' AS DB,COUNT(a.Name) AS Name,0 AS ID
FROM TblTestDB a
INNER JOIN TblDB2 b ON a.Name = b.Name
UNION
SELECT 'TblDB3' AS DB,COUNT(a.Name) AS Name,0 AS ID
FROM TblTestDB a
INNER JOIN TblDB3 b ON a.Name = b.Name
UNION
SELECT 'TblDB1' AS DB,0 AS Name,COUNT(a.ID) AS ID
FROM TblTestDB a
INNER JOIN TblDB1 b ON a.ID = b.ID
UNION
SELECT 'TblDB2' AS DB,0 AS Name,COUNT(a.ID) AS ID
FROM TblTestDB a
INNER JOIN TblDB2 b ON a.ID = b.ID
UNION
SELECT 'TblDB3' AS DB,0 AS Name,COUNT(a.ID) AS ID
FROM TblTestDB a
INNER JOIN TblDB3 b ON a.ID = b.ID
) a
GROUP BY DB
Issue: I may get more than 10 columns to show like matrix/pivot, the above query grows as per the columns list.
Any better way to make it short and simple.
SELECT DISTINCT 'TblDB1' AS DB
,COUNT(CASE WHEN a.Name = b.Name THEN 1 ELSE NULL END) AS Name
,COUNT(CASE WHEN a.ID = b.ID THEN 1 ELSE NULL END) AS ID
FROM TblTestDB a
CROSS JOIN TblDB1 b
UNION ALL
SELECT DISTINCT 'TblDB2' AS DB
,COUNT(CASE WHEN a.Name = b.Name THEN 1 ELSE NULL END) AS Name
,COUNT(CASE WHEN a.ID = b.ID THEN 1 ELSE NULL END) AS ID
FROM TblTestDB a
CROSS JOIN TblDB2 b
UNION ALL
SELECT DISTINCT 'TblDB3' AS DB
,COUNT(CASE WHEN a.Name = b.Name THEN 1 ELSE NULL END) AS Name
,COUNT(CASE WHEN a.ID = b.ID THEN 1 ELSE NULL END) AS ID
FROM TblTestDB a
CROSS JOIN TblDB3 b
This simple enough?
SELECT
tableName, sum(s.[Name]) as ncnt, max(s.id) as ID
FROM TblTestDB t
cross apply (
SELECT tableName, count(name) as [name], max(id) as ID
FROM (
SELECT 'TblDB1' as tid, * FROM TblDB1
union
SELECT 'TblDB2' as tid, * FROM TblDB2
union
SELECT 'TblDB3' as tid, * FROM TblDB3
) i
where i.name = t.name
group by tableName
) s
group by tableName
Not sure what you mean by, "I may get more than 10 columns to show like matrix/pivot".
Eg, if you're going to add a Surname column, should that be matched independently, or in combination with the Name column? (The former might get a bit complicated...)

Unexpected result using CTE to perform a random join on two tables for all rows one-to-many

I am attempting to randomly join the rows of two tables (TableA and TableB) such that each row in TableA is joined to only one row in TableB and every row in TableB is joined to at least one row in TableA.
For example, a random join of TableA with 5 distinct rows and TableB with 3 distinct rows should result in something like this:
TableA TableB
1 3
2 1
3 1
4 2
5 1
However, sometimes not all the rows from TableB are included in the final result; so in the example above might have row 2 from TableB missing because in its place is either row 1 or 3 joined to row 4 on TableA. You can see this occur by executing the script a number of times and checking the result. It seems that it is necessary for some reason to use an interim table (#Q) to be able to ensure that a correct result is returned which has all rows from both TableA and TableB.
Can someone please explain why this is happening?
Also, can someone please advise on what would be a better way to get the desired result?
I understand that sometimes no result is returned due to a failure of some kind in the cross apply and ordering which i have yet to identify and goes to the point that I am sure there is a better way to perform this operation. I hope that makes sense. Thanks in advance!
declare #TableA table (
ID int
);
declare #TableB table (
ID int
);
declare #Q table (
RN int,
TableAID int,
TableBID int
);
with cte as (
select
1 as ID
union all
select
ID + 1
from cte
where ID < 5
)
insert #TableA (ID)
select ID from cte;
with cte as (
select
1 as ID
union all
select
ID + 1
from cte
where ID < 3
)
insert #TableB (ID)
select ID from cte;
select * from #TableA;
select * from #TableB;
with cte as (
select
row_number() over (partition by TableAID order by newid()) as RN,
TableAID,
TableBID
from (
select
a.ID as TableAID,
b.ID as TableBID
from #TableA as a
cross apply #TableB as b
) as M
)
select --All rows from TableB not always included
TableAID,
TableBID
from cte
where RN in (
select
top 1
iCTE.RN
from cte as iCTE
group by iCTE.RN
having count(distinct iCTE.TableBID) = (
select count(1) from #TableB
)
)
order by TableAID;
with cte as (
select
row_number() over (partition by TableAID order by newid()) as RN,
TableAID,
TableBID
from (
select
a.ID as TableAID,
b.ID as TableBID
from #TableA as a
cross apply #TableB as b
) as M
)
insert #Q
select
RN,
TableAID,
TableBID
from cte;
select * from #Q;
select --All rows from both TableA and TableB included
TableAID,
TableBID
from #Q
where RN in (
select
top 1
iQ.RN
from #Q as iQ
group by iQ.RN
having count(distinct iQ.TableBID) = (
select count(1) from #TableB
)
)
order by TableAID;
See if this gives you what you're looking for...
DECLARE
#CountA INT = (SELECT COUNT(*) FROM #TableA ta),
#CountB INT = (SELECT COUNT(*) FROM #TableB tb),
#MinCount INT;
SELECT #MinCount = CASE WHEN #CountA < #CountB THEN #CountA ELSE #CountB END;
WITH
cte_A1 AS (
SELECT
*,
rn = ROW_NUMBER() OVER (ORDER BY NEWID())
FROM
#TableA ta
),
cte_B1 AS (
SELECT
*,
rn = ROW_NUMBER() OVER (ORDER BY NEWID())
FROM
#TableB tb
),
cte_A2 AS (
SELECT
a1.ID,
rn = CASE WHEN a1.rn > #MinCount THEN a1.rn - #MinCount ELSE a1.rn end
FROM
cte_A1 a1
),
cte_B2 AS (
SELECT
b1.ID,
rn = CASE WHEN b1.rn > #MinCount THEN b1.rn - #MinCount ELSE b1.rn end
FROM
cte_B1 b1
)
SELECT
A = a.ID,
B = b.ID
FROM
cte_A2 a
JOIN cte_B2 b
ON a.rn = b.rn;

How to join two Tables which have no unique ID

I am trying to Left join Table 1 to table 2
Table1 Table2
ID Data ID Data2
1 r 1 q
2 t 1 a
3 z 2 x
1 u 3 c
After i have left joined this two Tables i would like get something like this
Table1+2
ID Data Data2
1 r a
2 t x
3 z c
1 u q
and NOT
Table1+2
ID Data Data2
1 r q
2 t x
3 z c
1 u q
and my question is: is there any possibility to tell table 2 that if u have used something for table 1, dont use it and give me next value. do i have to make it im T-SQL or to and new column where i can list if this id exists then write 2 if not 1(Number data). How can i solve this problem?
Ty u in advance.
Since there's no uniqueness in the ID on both tables, lets add some uniqueness to it.
So it can be used to join on.
The window function ROW_NUMBER can be used for that.
An example solution that gives the expected result:
DECLARE #TestTable1 TABLE (ID INT, Data VARCHAR(1));
DECLARE #TestTable2 TABLE (ID INT, Data VARCHAR(1));
INSERT INTO #TestTable1 VALUES (1,'r'),(2,'t'),(3,'z'),(1,'u');
INSERT INTO #TestTable2 VALUES (1,'q'),(1,'a'),(2,'x'),(3,'c');
select
t1.ID, t1.Data,
t2.Data as Data2
from (
select ID, Data,
row_number() over (partition by ID order by Data) as rn
from #TestTable1
) t1
left join (
select ID, Data,
row_number() over (partition by ID order by Data) as rn
from #TestTable2
) t2 on t1.ID = t2.ID and t1.rn = t2.rn;
Note: Because of the LEFT JOIN, this does assume the amount of same ID's in table2 are equal or lower can those on table1. But you can change that to a FULL JOIN if that's not the case.
Returns :
ID Data Data2
1 r a
1 u q
2 t x
3 z c
To get the other result could have been achieved in different ways.
This is actually a more common situation.
Where one wants all from Table 1, but only get one value from Table 2 for each record of Table 1.
1) A top 1 with ties in combination with a order by rownumber()
select top 1 with ties
t1.ID, t1.Data,
t2.Data as Data2
from #TestTable1 t1
left join #TestTable2 t2 on t1.ID = t2.ID
order by row_number() over (partition by t1.ID, t1.Data order by t2.Data desc);
The top 1 with ties will only show those where the row_number() = 1
2) Using the row_number in a subquery:
select ID, Data, Data2
from (
select
t1.ID, t1.Data,
t2.Data as Data2,
row_number() over (partition by t1.ID, t1.Data order by t2.Data desc) as rn
from #TestTable1 t1
left join #TestTable2 t2 on t1.ID = t2.ID
) q
where rn = 1;
3) just a simple group by and a max :
select t1.ID, t1.Data, max(t2.Data) as Data2
from #TestTable1 t1
left join #TestTable2 t2 on t1.ID = t2.ID
group by t1.ID, t1.Data
order by 1,2;
All 3 give the same result:
ID Data Data2
1 r q
1 u q
2 t x
3 z c
Use ROW_NUMBER
DECLARE #Table1 TABLE (ID INT, DATA VARCHAR(10))
DECLARE #Table2 TABLE (ID INT, DATA VARCHAR(10))
INSERT INTO #Table1
VALUES
(1, 'r'),
(2, 't'),
(3, 'z'),
(1, 'u')
INSERT INTO #Table2
VALUES
(1, 'q'),
(1, 'a'),
(2, 'x'),
(3, 'c')
SELECT
A.*,
B.DATA
FROM
(SELECT *, ROW_NUMBER() OVER (PARTITION BY ID ORDER BY(SELECT NULL)) RowId FROM #Table1) A INNER JOIN
(SELECT *, ROW_NUMBER() OVER (PARTITION BY ID ORDER BY(SELECT NULL)) RowId FROM #Table2) B ON A.ID = B.ID AND
A.RowId = B.RowId

Combine two tables in SQL Server

I have tow tables with the same number of rows
Example:
table a:
1,A
2,B
3,C
table b:
AA,BB
AAA,BBB,
AAAA,BBBB
I want a new table made like that in SQL SErver:
1,A,AA,BB
2,B,AAA,BBB
3,C,AAAA,BBBB
How do I do that?
In SQL Server 2005 (or newer), you can use something like this:
-- test data setup
DECLARE #tablea TABLE (ID INT, Val CHAR(1))
INSERT INTO #tablea VALUES(1, 'A'), (2, 'B'), (3, 'C')
DECLARE #tableb TABLE (Val1 VARCHAR(10), Val2 VARCHAR(10))
INSERT INTO #tableb VALUES('AA', 'BB'),('AAA', 'BBB'), ('AAAA', 'BBBB')
-- define CTE for table A - sort by "ID" (I just assumed this - adapt if needed)
;WITH DataFromTableA AS
(
SELECT ID, Val, ROW_NUMBER() OVER(ORDER BY ID) AS RN
FROM #tablea
),
-- define CTE for table B - sort by "Val1" (I just assumed this - adapt if needed)
DataFromTableB AS
(
SELECT Val1, Val2, ROW_NUMBER() OVER(ORDER BY Val1) AS RN
FROM #tableb
)
-- create an INNER JOIN between the two CTE which just basically selected the data
-- from both tables and added a new column "RN" which gets a consecutive number for each row
SELECT
a.ID, a.Val, b.Val1, b.Val2
FROM
DataFromTableA a
INNER JOIN
DataFromTableB b ON a.RN = b.RN
This gives you the requested output:
You could do a rank over the primary keys, then join on that rank:
SELECT RANK() OVER (table1.primaryKey),
T1.*,
T2.*
FROM
SELECT T1.*, T2.*
FROM
(
SELECT RANK() OVER (table1.primaryKey) [rank], table1.* FROM table1
) AS T1
JOIN
(
SELECT RANK() OVER (table2.primaryKey) [rank], table2.* FROM table2
) AS T2 ON T1.[rank] = T2.[rank]
Your query is strange, but in Oracle you can do this:
select a.*, tb.*
from a
, ( select rownum rn, b.* from b ) tb -- temporary b - added rn column
where a.c1 = tb.rn -- assuming first column in a is called c1
if there is not column with numbers in a you can do same trick twice
select ta.*, tb.*
from ( select rownum rn, a.* from a ) ta
, ( select rownum rn, b.* from b ) tb
where ta.rn = tb.rn
Note: be aware that this can generate random combination, for example
1 A AA BB
2 C A B
3 B AAA BBB
because there is no order by in ta and tb

Resources