Please help me to write this query in SQL Server - sql

Consider this table :
dt qnt
---------- -------
1 10
2 -2
3 -4
4 3
5 -1
6 5
How do I create a query to get this result? (res is a running total column):
dt qnt res
---- ----- -----
1 10 10
2 -2 8
3 -4 4
4 3 7
5 -1 6
6 5 11

You can do it using a simple subquery that calculates the sum up to the current row, which should work well on any version of SQL Server;
SELECT dt, qnt,
(SELECT SUM(qnt) FROM Table1 ts WHERE ts.dt <= t1.dt) res
FROM Table1 t1
ORDER BY dt;
An SQLfiddle to test with.
If you're using SQL Server 2012, see Amit's answer for a more efficient query.

If you are Using sql Server 2012 than you can try like this.
Select * ,Sum([qnt]) Over(order by dt) from table1
Sql Fiddle Demo

Use one of this way :
CREATE TABLE [dbo].[T1]
(
[dt] [int] IDENTITY(1, 1)
NOT NULL ,
qnt [int] NULL ,
CONSTRAINT [PK_T1] PRIMARY KEY CLUSTERED ( [dt] ASC )
WITH ( PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON ) ON [PRIMARY]
)
ON [PRIMARY]
GO
INSERT INTO dbo.T1 ( qnt ) VALUES ( 10 )
INSERT INTO dbo.T1 ( qnt ) VALUES ( -2 )
INSERT INTO dbo.T1 ( qnt ) VALUES ( -4 )
INSERT INTO dbo.T1 ( qnt ) VALUES ( 3 )
INSERT INTO dbo.T1 ( qnt ) VALUES ( -1 )
INSERT INTO dbo.T1 ( qnt ) VALUES ( 5 )
GO
SELECT * ,
RunningSum = T1.qnt + COALESCE(( SELECT SUM(qnt)
FROM T1 AS T1Sub
WHERE ( T1Sub.dt < T1.dt )
), 0)
FROM T1
Go
SELECT T1.dt ,
T1.qnt ,
SUM(T1Inner.qnt)
FROM T1
INNER JOIN T1 AS T1Inner ON ( T1.dt >= T1Inner.dt )
GROUP BY T1.dt ,
T1.qnt
ORDER BY T1.dt ,
T1.qnt
GO
SELECT T1.* ,
T2.RunningSum
FROM T1
CROSS APPLY ( SELECT SUM(qnt) AS RunningSum
FROM T1 AS CAT1
WHERE ( CAT1.dt <= T1.dt )
) AS T2
Go
SELECT * ,
RunningSum = ( SELECT SUM(qnt)
FROM T1 AS T1In
WHERE ( T1In.dt <= T1.dt )
)
FROM T1
Go
-- In Sql Server 2012
SELECT * ,
SUM(T1.qnt) OVER ( ORDER BY T1.dt
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW )
AS RunningTotal
FROM T1
Drop Table [dbo].[T1]

Related

Return random value for each row from different table

I'm trying to get random name for each row, but it just shows same random value for every row. What am I missing?
SELECT TOP (1000) v.id,v.[Name], RandomName
FROM [V3_Priva].[dbo].[Vehicle] v
cross join
(Select top 1 ISNULL([Description_cs-CZ], [Description]) RandomName
from crm.Enumeration e
join crm.EnumerationType et on e.EnumerationType_FK = et.Id
where EnumerationType_FK = 12
order by NEWID()) RandomName
Result table
Try using something like the following to drive your lookup.
DECLARE #Rows AS TABLE ( I INT NOT NULL )
DECLARE #Vals AS TABLE ( ID INT IDENTITY, Val NVARCHAR(255) NOT NULL )
INSERT INTO #Rows
VALUES ( 0 )
, ( 1 )
, ( 2 )
, ( 3 )
, ( 4 )
, ( 5 )
, ( 6 )
, ( 7 )
, ( 8 )
, ( 9 )
INSERT INTO #Vals
VALUES ( 'Apple' )
, ( 'Banana' )
, ( 'Peach' )
, ( 'Plum' )
, ( 'Pear' )
WITH cte AS ( SELECT *, ABS(CHECKSUM(NEWID())) % 5 ID FROM #Rows )
SELECT cte.I
, cte.ID
, V.ID
, V.Val
FROM cte
JOIN #Vals V ON V.ID = cte.ID + 1
ORDER BY I
This way new ID is generated for each row, rather than once for the lookup.

Assign a unique ID to groups of rows that connect over 2 columns in SQL [duplicate]

This question already has answers here:
Group All Related Records in Many to Many Relationship, SQL graph connected components
(6 answers)
Closed 1 year ago.
My situation is the following:
I've a table with 2 ID-Columns and I want to assign a unique ID to rows that link by either one.
Here is an example of 6 rows in my table. All of these rows need to get the same unique ID.
Row
ID1
ID2
1
A
1
2
A
2
3
B
2
4
B
3
5
C
3
6
C
4
Rows 1 and 2 need to get the same unique id because they have the same ID1.
Row 3 needs to get that as well because its ID2 matches the ID2 of row 2.
Row 4 needs to get that as well because its ID1 matches the ID1 of row 3.
Row 5 needs to get that as well because its ID2 matches the ID2 of row 4.
Row 6 needs to get that as well because its ID1 matches the ID1 of row 5.
Basically the two columns form a chain and I want to assign an ID to that chain.
Is there some reasonably efficient way to do this in SQL?
Okay. This is really clumsy solution (and I should probably hand in my SQL Server badge for even suggesting it), but I think it'll get you over the line. I just hope you're not running this on a very large data set.
First up, I created some dummy temp tables to mimic your data (plus a few extra rows):
DROP TABLE IF EXISTS #Tbl1 ;
CREATE TABLE #Tbl1
(
[id] TINYINT NOT NULL
, [ID1] CHAR(1) NOT NULL
, [ID2] TINYINT NOT NULL
) ;
INSERT
INTO #Tbl1 ( [id], [ID1], [ID2] )
VALUES ( 1, 'A', 1 ), ( 2, 'A', 2 ), ( 3, 'B', 2 )
, ( 4, 'B', 3 ), ( 5, 'C', 3 ), ( 6, 'C', 4 )
, ( 7, 'D', 5 ), ( 8, 'D', 5 ), ( 9, 'E', 6 ) ;
Then, using a CTE and the LAG function, I identified which rows would see an increment of a unique identifier, and dumped all this in temp table:
DROP TABLE IF EXISTS #Tbl2 ;
WITH cte_Lags AS
(
SELECT [id]
, [ID1]
, LAG ( [ID1], 1, '' )
OVER ( ORDER BY [ID1] ASC, [ID2] ASC ) AS [ID1_lag]
, [ID2]
, LAG ( [ID2], 1, 0 )
OVER ( ORDER BY [ID1] ASC, [ID2] ASC ) AS [ID2_lag]
FROM #Tbl1
)
SELECT [id] AS [row]
, [ID1]
, [ID2]
, CASE
WHEN [ID1] = [ID1_lag]
OR [ID2] = [ID2_lag]
THEN 0
ELSE 1
END AS [incr_id]
INTO #Tbl2
FROM cte_Lags ;
I then add a column for your unique ID to the temp table:
ALTER TABLE #Tbl2 ADD [unique_id] TINYINT NULL ;
Now this is where it gets real messy!
I create a iterative loop that cycles through each row of the temp table and updates the unique_id column with the appropriate number, incremented only if the row is flagged to be incremented:
DECLARE #RowNum AS TINYINT ;
DECLARE #i AS TINYINT = 0 ;
WHILE ( ( SELECT COUNT(*) FROM #Tbl2 WHERE [unique_id] IS NULL ) > 0 )
BEGIN
SELECT TOP(1) #RowNum = [row]
FROM #Tbl2
WHERE [unique_id] IS NULL
ORDER BY [ID1] ASC, [ID2] ASC, [row] ASC ;
IF ( ( SELECT [incr_id] FROM #Tbl2 WHERE [row] = #RowNum ) = 1 )
SET #i += 1 ;
UPDATE #Tbl2
SET [unique_id] = #i
WHERE [row] = #RowNum ;
END
SELECT [row]
, [ID1]
, [ID2]
, [unique_id]
FROM #Tbl2
ORDER BY [ID1] ASC, [ID2] ASC ;
Now this all assumes that data doesn't repeat further down the table -- e.g. ('A', 1) is not going to reappear at row 50. If it does, this'll all need a little rethink.
I really hope someone cleverer than I can do this for you in a simple recursive CTE or a funky grouping function. But until then, this'll keep the boss happy.
A recursive CTE is useful for something like this.
Traverse through the records, then group the results.
WITH RCTE AS (
SELECT
[Row] AS BaseRow
, ID1 AS BaseID1
, ID2 AS BaseID2
, 1 AS Lvl
, [Row], ID1, ID2
FROM YourTable
UNION ALL
SELECT
c.BaseRow
, c.BaseID1
, c.BaseID2
, c.Lvl + 1
, t.[Row], t.ID1, t.ID2
FROM RCTE c
JOIN YourTable t
ON t.[Row] < c.[Row]
AND (t.ID2 = c.ID2 OR (t.ID2 < c.ID2 AND t.ID1 = c.ID1))
)
SELECT
BaseRow AS [Row]
, BaseID1 AS ID1
, MIN(ID2) AS ID2
FROM RCTE
GROUP BY BaseRow, BaseID1
ORDER BY BaseRow, BaseID1;
db<>fiddle here

UNION CTE with ORDER BY on 3 Tables

This is not easy to explain, however, I do believe there is a much nicer way to do what I managed to do and hope to get some help.
I have 3 tables (T1, T2, and T3). I need to get the latest from T1 and T2, then with those results return the results from t3 or from the previous result if t3 is empty. So the LatestDate doesn't really matter if there is a record in t3. Also, if there is no data in t3 and the LatestDate is the same on t1 and t2 (rarely will happen, but want to plan accordingly), I want results from t1.
Here's a sample of what I got, mind you the actual query is has many more fields, but the concept is the same.
CREATE TABLE [dbo].[t1](
[Id] [INT] NOT NULL,
[LatestDate] [DATETIME] NOT NULL
);
CREATE TABLE [dbo].[t2](
[Id] [INT] NOT NULL,
[LatestDate] [DATETIME] NOT NULL
);
CREATE TABLE [dbo].[t3](
[Id] [INT] NOT NULL,
[LatestDate] [DATETIME] NOT NULL
);
INSERT t1 (Id, LatestDate) VALUES (1, CAST(N'2000-01-01T00:00:00.000' AS DateTime));
INSERT t1 (Id, LatestDate) VALUES (2, CAST(N'2001-01-01T00:00:00.000' AS DateTime));
INSERT t1 (Id, LatestDate) VALUES (3, CAST(N'2002-01-01T00:00:00.000' AS DateTime));
INSERT t2 (Id, LatestDate) VALUES (1, CAST(N'2001-01-01T00:00:00.000' AS DateTime));
INSERT t2 (Id, LatestDate) VALUES (2, CAST(N'2002-01-01T00:00:00.000' AS DateTime));
INSERT t2 (Id, LatestDate) VALUES (4, CAST(N'2003-01-01T00:00:00.000' AS DateTime));
INSERT t3 (Id, LatestDate) VALUES (1, CAST(N'2001-01-01T00:00:00.000' AS DateTime));
INSERT t3 (Id, LatestDate) VALUES (2, CAST(N'2000-01-01T00:00:00.000' AS DateTime));
INSERT t3 (Id, LatestDate) VALUES (5, CAST(N'2004-01-01T00:00:00.000' AS DateTime));
GO;
WITH CTE AS (
SELECT TOP 1 * FROM (
SELECT 2 AS Sort, * FROM t1 WHERE t1.id = #UserId
UNION
SELECT 3 AS Sort, * FROM t2 WHERE t2.id = #UserId
) AS t
ORDER BY
t.LatestDate DESC
)
SELECT TOP 1 * FROM (
SELECT TOP 1 * FROM CTE
UNION
SELECT 1 AS Sort, * FROM t3 WHERE t3.id = #UserId
) AS t
ORDER BY
t.Sort;
Expected results:
When #UserID = 1:
Sort Source Id LatestDate
1 t3 1 1/1/2001
When #UserID = 2:
Sort Source Id LatestDate
1 t3 2 1/1/2000
When #UserID = 3:
Sort Source Id LatestDate
2 t1 3 1/1/2002
When #UserID = 4:
Sort Source Id LatestDate
3 t2 4 1/1/2003
When #UserID = 5:
Sort Source Id LatestDate
1 t3 5 1/1/2004
Thanks!
If I understand you correctly you want something like:
DECLARE #UserID INT = 5;
WITH cte AS (
SELECT 1 AS src, 1 as [tbl], * FROM t1 WHERE id = #UserId
UNION ALL SELECT 1, 2, * FROM t2 WHERE id = #UserId
UNION ALL SELECT 3, 3, * FROM t3 WHERE id = #UserId
), cte2 AS (
SELECT * , ROW_NUMBER() OVER(ORDER BY src DESC, LatestDate DESC, tbl ASC) AS rn
FROM cte
)
SELECT Id, LatestDate
FROM cte2
WHERE rn = 1;
RextesterDemo
Using PARTITION BY you could move filtering #userId to final part:
DECLARE #UserID INT = 5;
WITH cte AS (
SELECT 1 AS src, 1 as [tbl], * FROM t1
UNION ALL SELECT 1, 2, * FROM t2
UNION ALL SELECT 3, 3, * FROM t3
), cte2 AS (
SELECT *
,ROW_NUMBER() OVER(PARTITION BY Id ORDER BY src DESC, LatestDate DESC, tbl ASC) AS rn
FROM cte
)
SELECT Id, LatestDate
FROM cte2
WHERE rn = 1
AND id = #UserID
Rextester Demo2
How's this?
This assumes you will take the t3 result if it exists
or the max result from T1 or T2 if not.
if exists(select * from t3 where ID= #ID)
(
select ID, Max(LatestDate), TN='T3'
from t3
where ID= #ID
)
else
(
select top 1 ID, Max(LatestDate) LD,TN
from (Select *,TN='t1' from T1
union all
Select *, TN='t2' from t2) as a
where id=#ID
group by ID,TN
order by LD desc
)

Select row name with max date with grouping

For example:
create table #t
(
[ID] int,
[Date] date,
[Name] varchar(5)
)
insert into #t
values
(1, getdate(),'1-1'),
(2, dateadd(D,-10,getdate()),'2-1'),
(2, dateadd(D,-5,getdate()),'2-2'),
(1, dateadd(M,-1,getdate()),'1-2')
select * from #t
I need to select [Name] for each [ID] with max [Data].
Something like this:
select [1], [2]
from ( select ID, [Date] from #t ) y
pivot (
max(y.[Date])
for y.ID in ([1],[2])
) pvt;
Output:
1 2
2017-04-28 2017-04-23
but instead of [Date] i want to see [Name]
what i want to view
1 2
1-1 2-2
Please help. Thank you.
Please try the following code
create table #t
(
[ID] int,
[Date] date,
[Name] varchar(5)
)
insert into #t
values
(1, getdate(),'1-1'),
(2, dateadd(D,-10,getdate()),'2-1'),
(2, dateadd(D,-5,getdate()),'2-2'),
(1, dateadd(M,-1,getdate()),'1-2')
select [1], [2]
from ( select ID, [Name] from #t ) y
pivot (
max(y.[Name])
for y.ID in ([1],[2])
) pvt;
drop table #t
You can use row_nubmber() with date desc and pivot as below:
;with cte as (
select id, RowN = row_number() over (partition by id order by date desc), name from #t
) select * from
(select id, name from cte where rown = 1 ) s
pivot (max(name) for id in ([1],[2])) p
You can try the following :
SELECT [1], [2]
FROM (SELECT y.ID,
t.Name
FROM (SELECT ID,
MAX([Date]) AS [Date]
FROM #t
GROUP BY ID ) y
INNER JOIN #t t ON y.[Date] = t.[Date]
) x
PIVOT
(
MAX(x.Name)
FOR x.ID IN ([1],[2])
) pvt;
You can see this here -> http://rextester.com/ZGQGSC94965
Hope this helps!!!
try this one.
CREATE TABLE #t
(
[ID] INT ,
[Date] DATE ,
[Name] VARCHAR(5)
)
INSERT INTO #t
VALUES (1, getdate(),'1-1'),
(2, dateadd(D,-10,getdate()),'2-1'),
(2, dateadd(D,-5,getdate()),'2-2'),
(1, dateadd(M,-1,getdate()),'1-2')
SELECT *
FROM #t;
WITH CTE
AS ( SELECT ID ,
MAX(Date) [Date]
FROM #t
GROUP BY ID
),
CTE2
AS ( SELECT cte.ID ,
cte.Date ,
t.name
FROM CTE
OUTER APPLY ( SELECT TOP 1
name
FROM #t
WHERE (ID = cte.ID AND Date = cte.Date)
) T
)
SELECT MAX([1]) [1] ,
MAX([2]) [2]
FROM ( SELECT ID ,
[Date] ,
NAME
FROM CTE2
) y PIVOT ( MAX(y.NAME) FOR y.ID IN ( [1], [2] ) ) pvt
Result
ID Date Name
----------- ---------- -----
1 2017-05-02 1-1
2 2017-04-22 2-1
2 2017-04-27 2-2
1 2017-04-02 1-2
(4 row(s) affected)
1 2
----- -----
1-1 2-2
(1 row(s) affected)

SQL close close Gaps in data over time

I have a table of play data that I'm using for a prototype. I'm generating the data while I'm at work, but when I leave and my machine goes to sleep, the data generation stops. This has cause large gaps in my collection of items.
I would like to be able to shift the values of each item in the DateTimeCreated collumn of my table so that there isn't a gap of more than 10 minutes between any item and the next generated item.
The structure of the table is like this:
CREATE TABLE [dbo].[Items](
[Id] [uniqueidentifier] NOT NULL,
[DateTimeCreated] [datetimeoffset](7) NOT NULL,
[AuthorId] [uniqueidentifier] NOT NULL,
[Source] [varchar](max) NOT NULL,
[FullText] [varchar](max) NOT NULL,
CONSTRAINT [PK_Items] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
I was thinking about doing this in L2S, but I have over 1 million records, so IDK if that is the best solution (iterating over each item). I know there has to be some way to do this in SQL that will be much faster.
An alternative Ranking-Functions Approach (not 100% tested):
DECLARE #tenMinutes AS INT = 600;
WITH StartingPoints AS
(
SELECT DateTimeCreated, ROW_NUMBER() OVER(ORDER BY DateTimeCreated) AS rownum
FROM dbo.Items AS A
WHERE NOT EXISTS(
SELECT * FROM dbo.Items AS B
WHERE B.DateTimeCreated < A.DateTimeCreated
AND DATEDIFF(SECOND,B.DateTimeCreated, A.DateTimeCreated) BETWEEN 0 AND #tenMinutes
)
),
EndingPoints AS
(
SELECT DateTimeCreated, ROW_NUMBER() OVER(ORDER BY DateTimeCreated) AS rownum
FROM dbo.Items AS A
WHERE NOT EXISTS(
SELECT * FROM dbo.Items AS B
WHERE A.DateTimeCreated < B.DateTimeCreated
AND DATEDIFF(SECOND,A.DateTimeCreated, B.DateTimeCreated) BETWEEN 0 AND #tenMinutes
)
),
Islands AS
(
SELECT S.DateTimeCreated AS start_range,
E.DateTimeCreated AS end_range,
ROW_NUMBER() OVER(ORDER BY S.DateTimeCreated) AS row_num
FROM StartingPoints AS S
JOIN EndingPoints AS E on E.rownum = S.rownum
),
Ofs AS
(
SELECT I2.start_range,
I2.end_range,
I1.end_range AS prev,
DATEDIFF(SECOND, I1.end_range, I2.start_range) AS offset
FROM Islands AS I1
JOIN Islands AS I2 ON I2.row_num = I1.row_num + 1 OR I2.row_num IS NULL
),
CmlOfs AS
(
SELECT O1.start_range,
O1.end_range,
O1.prev,
O1.offset,
(SELECT SUM(O2.offset) FROM Ofs AS O2
WHERE O2.start_range <= O1.start_range) AS cum_offset
FROM Ofs AS O1
),
UpdateQ AS
(
SELECT Items.*, DATEADD(SECOND, -1 * CmlOfs.cum_offset, Items.DateTimeCreated) AS new_value
FROM Items
JOIN CmlOfs ON Items.DateTimeCreated BETWEEN CmlOfs.start_range AND CmlOfs.end_range
)
UPDATE UpdateQ
SET DateTimeCreated = new_value;
Make sure to have an index on DateTimeCreated if you want this to be anything other than a pig.
It also assumes (as you said in your comment) there are few gaps compared to total number of records.
WITH
gap (Start,Finish)
AS
(
SELECT
DateTimeCreated,
(SELECT MIN(DateTimeCreated) FROM items AS lookup WHERE DateTimeCreated > DateTimeCreated)
FROM
items
WHERE
DATEADD(second, 600, DateTimeCreated) < (SELECT MIN(DateTimeCreated) FROM items AS lookup WHERE DateTimeCreated > DateTimeCreated)
UNION ALL
SELECT
MAX(DateTimeCreated),
MAX(DateTimeCreated)
FROM
items
)
,
offset (Start,Finish,Offset)
AS
(
SELECT
[current].Start,
(SELECT MIN(Start) FROM gap WHERE Start > [current].Start),
DATEDIFF(second, Start, Finish) - 600
FROM
gap AS [current]
)
,
cumulative_offset (Start,Finish,Offset)
AS
(
SELECT
[current].Start,
[current].Finish,
SUM([cumulative].Offset)
FROM
offset AS [current]
INNER JOIN
offset AS [cumulative]
ON [cumulative].Start <= [current].Start
)
UPDATE
items
FROM
cumulative_offset
SET
DateTimeCreated = DATEADD(second, -Offset, DateTimeCreated)
INNER JOIN
items
ON items.DateTimeCreated > cumulative.Start
AND items.DateTimeCreated <= cumulative.Finish