Generate sequence number comparing adjacent rows in TSQL - sql

I need help in generating sequence number when group name changes in adjacent rows. I already tried DENSE RANK but it did not work.
Group || Sequence Number
========================
A || 1 7/1/2012
A || 2 7/2/2012
A || 3 7/2/2012
B || 1 7/3/2012
B || 2 7/3/2012
B || 3 7/3/2012
A || 1 7/4/2012
A || 2 7/5/2012
A || 3 7/5/2012
C || 1
B || 1
B || 2
C || 1
C || 2
Thanks

Here's a couple of solutions - one simple, one more complex but closer matching your question:
--if you want all As grouped first, then all Bs, etc
select *
, ROW_NUMBER() over (partition by [group] order by id) SequenceNumber
from demo
--if you want the more complex solution where the different groups of As are kept apart from one another
select id
, [group]
, ROW_NUMBER() over (partition by x.p order by x.id) sequenceNumber
from (
select id
, [group]
, (
select min(b.id)
from demo b
where b.[group] <> a.[group]
and b.id > a.id
) p
from demo a
) x
order by id
Code to setup / run the above sample:
create table demo
(
id bigint identity(1,1) not null primary key clustered
, [group] nchar not null
)
go
insert demo
select 'A'
union all select 'A'
union all select 'A'
union all select 'B'
union all select 'B'
union all select 'B'
union all select 'C'
union all select 'C'
union all select 'C'
union all select 'A' --in your example you seemed to alow a second group of As separate to the first
union all select 'A'
union all select 'A'
union all select 'A'
union all select 'C'
go

This should work, you can do a while loop.
declare #t table (
id int identity primary key,
yourgroup char,
grouprank int
);
insert into #t (yourgroup)
select yourgroup
from yourtable;
declare #lastgroup char,
#newrank int,
#i int = (select MIN(id) from #t),
#end int = (select MAX(id) from #t);
while #i <= #end begin
if #lastgroup = (select yourgroup
from #t
where id = #i) begin
set #newrank += 1;
end else begin
set #newrank = 1;
end;
select #lastgroup = yourgroup
from #t
where id = #i;
update #t
set grouprank = #newrank
where id = #i;
set #i += 1;
end;
select * from #t;

Sorry for the slow reply to your last comment; I've been at work/away for the start of the weekend. What you're after can be achieved based on my previous answer, but I suspect the code below would be much more efficient / readable. The drawback of the below code is that this does rely on the new SQL 2012 LAG and LEAD features.
You can read up on these features here: http://blog.sqlauthority.com/2011/11/15/sql-server-introduction-to-lead-and-lag-analytic-functions-introduced-in-sql-server-2012/
Info on SQL 2012 licensing here, should you choose to upgrade: http://www.microsoft.com/sqlserver/en/us/get-sql-server/how-to-buy.aspx
Obviously there are many reasons why upgrading may not be justifiable, but thought I'd provide this answer in case it's an option available to you / others looking for this solution:
--Sample Data Setup:
if object_id('demo') is not null drop table demo
go
create table demo
(
id bigint identity(1,1) not null primary key clustered
, groupId nchar not null
, startDate date not null constraint uk_demo_startDate unique
)
go
insert demo
select 'A', '2009-01-01'
union all select 'A', '2009-01-02'
union all select 'A', '2009-02-01'
union all select 'B', '2009-03-01'
union all select 'B', '2009-04-01'
union all select 'B', '2009-05-01'
union all select 'C', '2009-06-01'
union all select 'C', '2009-07-01'
union all select 'C', '2009-08-01'
union all select 'A', '2009-09-01'
union all select 'A', '2009-10-01'
union all select 'A', '2009-11-01'
union all select 'A', '2009-12-01'
union all select 'C', '2010-01-01'
union all select 'D', '2010-01-02'
union all select 'D', '2010-01-03'
union all select 'D', '2010-01-04'
union all select 'E', '2010-01-05'
union all select 'E', '2010-01-06'
union all select 'D', '2010-01-07'
union all select 'D', '2010-01-08'
union all select 'E', '2010-01-09'
union all select 'E', '2010-01-10'
union all select 'D', '2011-01-01'
union all select 'D', '2011-01-02'
union all select 'E', '2012-01-01'
union all select 'X', '2012-01-02'
union all select 'D', '2012-01-03'
go
--Actual Solution
select *
, noDays + noDaysAtStatusAtStart noDaysAtStatusAtEnd
from
(
select id
, groupId
, startDate
, noDays
, case
when groupId = previousGroupId then lag(noDays,1) over (order by startDate)
--when previousGroupId is null then 0 --covered by else
else 0
end noDaysAtStatusAtStart
from
(
select id
, startDate
, groupId
, endDate
, previousGroupId
, dateDiff(day,startDate,endDate) noDays
from
(
select id
, startDate
, groupId
, lead(startDate,1) over (order by startDate) endDate
, lag(groupId,1) over (order by startDate) previousGroupId
from demo
) x
) y
) z
order by z.startDate

Related

Select data from one table base on selection from another table in SQL

I have these 3 table
First contain the item with price on given dates
2nd is the table of items
3rd is the table of dates in which we want to show the price of 2nd table item on every date
if duration is not available on first table it should be 0
with myTable ( item,startdate,enddate,price) as
(
select 'AAAA' ,'16-3-2020','19-3-2020','50' union all
select 'AAAA' ,'16-4-2020','19-4-2020','70' union all
select 'BBB' ,'16-3-2020','19-3-2020','20' union all
select 'BBB' ,'16-4-2020','19-4-2020','90' union all
select 'CCC' ,'16-3-2020','29-3-2020','45' union all
select 'CCC' ,'16-4-2020','19-4-2020','120'
)
select item,startdate,enddate,price from myTable
GO
with itemTable ( item) as
(
select 'AAAA' union all
select 'BBB' union all
select 'CCC'
)
select item from itemTable
GO
with DateTable ( dateItem) as
(
select '16-3-2020' union all
select '19-4-2020' union all
select '20-3-2020'
)
select dateItem from DateTable
GO
and my desire result should be like this (above is dynamic data)
with mydesireTable (item, [16-3-2020],[19-4-2020],[20-3-2020]) as
(
select 'AAAA' ,'50','70','0' union all ---0 as its not on above data in duration
select 'BBB' ,'20','90','0' union all
select 'CCC' ,'45','120','45'
)
select item, [16-3-2020],[19-4-2020],[20-3-2020] from mydesireTable
I am not sure what to search for :) as i want to write query for it which return my desire table as data ( or as in temporary table )
One of many ways to do this. This is a static crosstab. You need to list out all the columns explicitly (twice)
If your columns are dynamic, you need to use a dynamic crosstab. You should also consider doing this in your "presentation" layer, i.e. excel or whatever you are handing this over in.
You should consider what you want when something in mytable appears against a bucket twice (this solution will add the prices)
with myTable ( item,startdate,enddate,price) as
(
select 'AAAA' ,CAST('2020-03-16' AS DATE),CAST('2020-03-19' AS DATE),50 union all
select 'AAAA' ,'2020-04-16','2020-04-19',70 union all
select 'BBB' ,'2020-03-16','2020-03-19',20 union all
select 'BBB' ,'2020-04-16','2020-04-19',90 union all
select 'CCC' ,'2020-03-16','2020-03-29',45 union all
select 'CCC' ,'2020-04-16','2020-04-19',120
),
itemTable ( item) as
(
select 'AAAA' union all
select 'BBB' union all
select 'CCC'
)
,DateTable ( dateItem) as
(
select CAST('2020-03-16' AS DATE) union all
select '2020-04-19' union all
select '2020-03-20'
)
SELECT item,
[2020-03-16],[2020-04-19], [2020-03-20]
FROM
(
select item, dateitem, price from myTable
inner join datetable on datetable.dateItem between mytable.startdate and myTable.enddate
) As Src
PIVOT
(
SUM(price)
FOR
dateitem IN ([2020-03-16],[2020-03-20],[2020-04-19])
) as P
IF OBJECT_ID('tempdb..#myTable', 'U') IS NOT NULL
DROP TABLE #myTable;
IF OBJECT_ID('tempdb..#itemTable', 'U') IS NOT NULL
DROP TABLE #itemTable;
IF OBJECT_ID('tempdb..#DateTable', 'U') IS NOT NULL
DROP TABLE #DateTable;
CREATE TABLE #myTable (
item VARCHAR(MAX) NOT NULL,
startdate DATE NOT NULL,
enddate DATE NOT NULL,
price INT NOT NULL DEFAULT(0)
);
INSERT #myTable (item, startdate, enddate, price) VALUES
('AAAA' ,CAST('2020-03-16' AS DATE),CAST('2020-03-19' AS DATE),50),
('AAAA' ,'2020-04-16','2020-04-19',70),
('BBB' ,'2020-03-16','2020-03-19',20),
('BBB' ,'2020-04-16','2020-04-19',90),
('CCC' ,'2020-03-16','2020-03-29',45),
('CCC' ,'2020-04-16','2020-04-19',120)
CREATE TABLE #itemTable (
item VARCHAR(MAX) NOT NULL
)
INSERT #itemTable (item) VALUES
('AAAA'),
('BBB'),
('CCC')
CREATE TABLE #DateTable (
dateItem DATE NOT NULL
)
INSERT #DateTable (dateItem) VALUES
(CAST('2020-03-16' AS DATE)),
(CAST('2020-04-19' AS DATE)),
(CAST('2020-03-20' AS DATE)),
(CAST('2020-03-21' AS DATE)),
(CAST('2021-03-21' AS DATE)),
(CAST('2022-03-21' AS DATE))
Declare #DynamicCol nvarchar(max),#DynamicColNull nvarchar(max)
,#Sql nvarchar(max)
SELECT #DynamicColNull=STUFF((SELECT DISTINCT ', '+'ISNULL('+QUOTENAME(dateItem),','+'''0'''+') As '+QUOTENAME(dateItem)
FROM #DateTable FOR XML PATH ('')),1,2,'')
SELECT #DynamicCol=STUFF((SELECT DISTINCT ', '+QUOTENAME(dateItem) FROM #DateTable FOR XML PATH ('')),1,2,'')
SET #Sql='SELECT [item], '+#DynamicColNull+' From
(
select item, dateitem, price from #myTable
inner join #datetable on #datetable.dateItem between #mytable.startdate and #myTable.enddate
)
AS Src
PIVOT
(
SUM(price) FOR [dateitem] IN ('+#DynamicCol+')
)AS Pvt'
PRINT #Sql
EXEC(#Sql)

Nested SQL Query from multiple tables and COUNT inline

Good day bloggers.
I want to create a query which will select car name, model, price from table tblCars. Within the same query result, I need to check from table tblLikes if certain user has liked the car. tblLikes has id, userId and carId (foreign key). So for a specific user I need something like the following in return.
1 Corolla Toyota R100,000 1 (user liked)
2 A3 Audi R700,000 0 (user did not like)
What Ι have tried is the following without success.
SELECT
CarName
, CarModel
, CarPrice
, (
SELECT COUNT(*)
FROM tblLikes
WHERE
userId = #userId
AND CarId = #CarID
) as Likes
FROM tblCars
SELECT COUNT(*)
FROM tblLikes
WHERE
userId = #userId
AND CarId = #CarID
Return 1 if the record exist and 0 if it does not.
declare #tblCars table (
id int IDENTITY(1,1) NOT NULL
, CarName varchar(20)
, CarModel varchar(20)
, CarPrice money
)
declare #tblLikes table (
id int IDENTITY(1,1) NOT NULL
, userId int
, carId int
)
insert into #tblCars (
CarName
, CarModel
, CarPrice
)
select 'BMW' , 'qqq', '1000'
union all select 'Mercedes' , 'www', '2000'
union all select 'Audi' , 'eee', '3000'
union all select 'Scania' , 'rrr', '4000'
union all select 'Opel' , 'ttt', '5000'
union all select 'Mazda' , 'yyy', '6000'
union all select 'Budatti' , 'uuu', '7000'
union all select 'Lamborgini' , 'iii', '8000'
union all select 'Tesla' , 'ooo', '9000'
union all select 'Space' , 'ppp', '10000'
insert into #tblLikes (
userId
, carId
)
select '1', '1'
union all select '1', '3'
union all select '1', '5'
union all select '1', '7'
union all select '1', '9'
select
c.*
, (
case when l.id is not null
then '1'
else '0'
end
) as likes
from #tblCars as c
left outer join #tblLikes as l
on l.carId = c.id

Unique Count - TSQL

CODE
CREATE TABLE #TEMP (ID INT, AVAIL BIT, FK INT, DT DATETIME);
INSERT INTO #TEMP (ID,AVAIL,FK,DT)
SELECT 1,1,1,GETDATE()
UNION ALL
SELECT 2,0,2,GETDATE()
UNION ALL
SELECT 3,1,3,GETDATE()
UNION ALL
SELECT 1,1,4,GETDATE()
UNION ALL
SELECT 4,0,5,GETDATE()
UNION ALL
SELECT 5,1,6,GETDATE();
CREATE TABLE #FK (FK INT, DT2 DATETIME)
INSERT INTO #FK (FK, DT2)
SELECT 1,NULL
UNION
SELECT 2,DATEADD(DAY,1,GETDATE())
UNION
SELECT 3,DATEADD(DAY,1,GETDATE())
UNION
SELECT 4,NULL
UNION
SELECT 5,NULL
UNION
SELECT 6,DATEADD(DAY,1,GETDATE())
UNION
SELECT 7,DATEADD(DAY,1,GETDATE())
SELECT
[TotalIds] = COUNT(DISTINCT ID)
,[TotalAvail] = SUM(CASE WHEN [AVAIL] = 1 THEN 1 ELSE 0 END)
,[DTDIFF] = SUM(DATEDIFF(DAY,T1.DT,F.DT2))
FROM #TEMP T1 INNER JOIN #FK F
ON T1.FK = F.FK;
DROP TABLE #TEMP;
DROP TABLE #FK;
OUTPUT
TotalIds TotalAvail DTDIFF
5 4 3
DESIRED OUTPUT
TotalIds TotalAvail DTDIFF
5 3 3
GOAL:
I want to get sum/count of UNIQUE IDs where [AVAIL] = 1.
I can do that by COUNT(DISTINCT ID) WHERE [AVAIL] = 1 BUT... I need to do that within this SUM since I'm querying other data within the same query.
Desired output = 3
(for ID 1, 3, and 5).
Updated with Current/Desired output.
Updated with more data.
You could change UNION ALL for UNION and remove the duplicates
But you mention otherValue, so maybe you need something like this
SELECT SUM(otherValue)
FROM (
SELECT DISTINCT ID, AVAIL, otherValue
FROM TEMP
WHERE [AVAIL] = 1
) T
CREATE TABLE #TEMP (ID INT, AVAIL BIT, FK INT, DT DATETIME);
INSERT INTO #TEMP (ID,AVAIL,FK,DT)
SELECT 1,1,1,GETDATE()
UNION ALL
SELECT 2,0,2,GETDATE()
UNION ALL
SELECT 3,1,3,GETDATE()
UNION ALL
SELECT 1,1,4,GETDATE()
UNION ALL
SELECT 4,0,5,GETDATE()
UNION ALL
SELECT 5,1,6,GETDATE();
CREATE TABLE #FK (FK INT, DT2 DATETIME)
INSERT INTO #FK (FK, DT2)
SELECT 1,NULL
UNION
SELECT 2,DATEADD(DAY,1,GETDATE())
UNION
SELECT 3,DATEADD(DAY,1,GETDATE())
UNION
SELECT 4,NULL
UNION
SELECT 5,NULL
UNION
SELECT 6,DATEADD(DAY,1,GETDATE())
UNION
SELECT 7,DATEADD(DAY,1,GETDATE())
SELECT
[TotalIds] = COUNT(DISTINCT ID)
,[TotalAvail] = COUNT(DISTINCT CASE WHEN [AVAIL] = 1 THEN ID ELSE NULL END)
,[DTDIFF] = SUM(DATEDIFF(DAY,T1.DT,F.DT2))
FROM #TEMP T1 INNER JOIN #FK F
ON T1.FK = F.FK;
DROP TABLE #TEMP;
DROP TABLE #FK;
Use the cte result for your further process.
;WITH CTE_Temp AS
(SELECT COUNT(DISTINCT ID) [TotalAvail]
FROM #TEMP
WHERE [Avail]=1)
SELECT [TotalAvail]
FROM CTE_Temp

How to insert different values from same row in multiple rows of a new table

I have the A table:
id bigint,
a integer,
b integer,
c integer,
date date
and I have to put every single value in a new table following by the date, in this way:
B table:
id bigint,
type integer,
date date
For example, if in my A table I had a row like this:
id a b c date
13 5 4 7 2014-11-09
I would like to put this values in B table like this:
id type date
1 5 2014-11-09,
2 4 2014-11-09,
3 7 2014-11-09
Any suggestions?
Unpivot first the data using UNION ALL and then assign a new id using ROW_NUMBER:
SELECT
ROW_NUMBER() OVER(ORDER BY id, col) AS id,
type,
date
FROM (
SELECT id, 'a' AS col, a AS type, date FROM tableA UNION ALL
SELECT id, 'b' AS col, b AS type, date FROM tableA UNION ALL
SELECT id, 'c' AS col, c AS type, date FROM tableA
) t
If the new id is auto generated, you do not need the ROW_NUMBER at all.
SELECT a AS type, date FROM tableA UNION ALL
SELECT b AS type, date FROM tableA UNION ALL
SELECT c AS type, date FROM tableA
INSERT INTO A
( id ,
a ,
b ,
c ,
date )
VALUES ( 13,
5 ,
4 ,
7 ,
2014-11-09
)
ALTER TRIGGER NewTrigger ON A
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #id AS NVARCHAR(50)
DECLARE #type AS NVARCHAR(50)
DECLARE #date AS INT
SELECT #Date = INSERTED.Date
FROM INSERTED
INSERT INTO B
( Date,
Type,
id
)
VALUES ( #Date,
4,
1
)
END
GO
SELECT * A
SELECT * FROM B
In Oracle you can try using REGEXP to simulate the sqame . Hope this helps.
SELECT DISTINCT LEVEL,
TRIM(regexp_substr(a.colk,'[^,]+', 1, level)) TYPE,
SYSDATE
FROM
(SELECT LEVEL,
COL1
||','
||COL2
||','
||COL3 colk,
SYSDATE
FROM
(SELECT 13 AS ID,5 COL1,4 AS COL2,7 AS COL3,sysdate AS dt FROM DUAL
)
CONNECT BY LEVEL <= REGEXP_COUNT(COL1
||','
||COL2
||','
||COL3,',')+1
)a
CONNECT BY regexp_substr(a.colk, '[^,]+', 1, level) IS NOT NULL;
Contest for shortest code :) My variant is:
insert into b(type, date)
select unnest(array[a,b,c]), date from a;
Assuming that the id column in the b table is autoincremented.

Fixed array and random numbers in an SQL query

What's the easiest way to select one of five fixed strings returned in an SQL Server query, randomly?
I.e. the equivalent of:
function randomColumn() {
var values = ['apple', 'banana', 'orange', 'cherry', 'lemon'];
var idx = Math.floor(Math.random() * 5);
return values[idx];
}
I need to change my existing SQL script to have a certain column return one of these values, without the need to change my client code.
Do I need to create a temp table?
I'm using SQL Server 2008 R2.
You don't need a temporary table for a few strings, you can create the result on the fly:
select str
from (
select 0 as id, 'apple' as str
union all
select 1, 'banana'
union all
select 2, 'orange'
union all
select 3, 'cherry'
union all
select 4, 'lemon'
) x
where id = floor(rand() * 5)
select top 1 Value
from Table1
order by newid()
SQL Fiddle Example
Select 'apple' values
INTO #tmp
UNION ALL
Select'banana' values
UNION ALL
Select 'orange' values
.
.
.
select top 1 values
from #tmp
order by newid()
OR
ALTER TABLE #tmp
ADD id BIGINT IDENTITY(1,1)
DECLARE #rand BIGINT
SET #rand=rand()*4
select top 1 values
from #tmp
where id=#rand
DECLARE #ListofIDs TABLE(IDs VARCHAR(100), ID INT IDENTITY(1,1));
INSERT INTO #ListofIDs
SELECT 'a'
UNION ALL
SELECT 'b'
UNION ALL
SELECT '20'
UNION ALL
SELECT 'c'
UNION ALL
SELECT '30'
UNION ALL
SELECT 'd';
SELECT * FROM #ListofIDs;
SELECT Ids from #ListofIDs where ID=1+ CONVERT(INT, (5)*RAND())