Summary table generation in SQL Server - sql

I have a table like this:
I want to see data in a summary view like this:
I need help for a T-SQL script. Thank you.
Sorry for my little English.

This works:
[Setup]
CREATE TABLE #PaymentTable ( Id INT IDENTITY, AccountGroupId INT, AccountId INT, Payment INT )
INSERT INTO #PaymentTable ( AccountGroupId, AccountId, Payment )
SELECT 1, 1, 5 UNION ALL SELECT 1, 1, 5 UNION ALL
SELECT 1, 2, 5 UNION ALL SELECT 2, 4, 5 UNION ALL
SELECT 2, 3, 5 UNION ALL SELECT 2, 3, 5 UNION ALL
SELECT 2, 4, 5
CREATE TABLE #Group ( AccountGroupId INT, GroupName VARCHAR(100) )
INSERT INTO #Group ( AccountGroupId, GroupName )
SELECT 1, 'Group 1' UNION Select 2, 'Group 2'
CREATE TABLE #Account ( AccountId INT, AccountName VARCHAR(100) )
INSERT INTO #Account ( AccountId, AccountName )
SELECT 1, 'John' UNION Select 2, 'Edvard' UNION
SELECT 3, 'David' UNION SELECT 4, 'Jimi'
[Query]
SELECT
[Group],
Account,
TotalPayment
FROM
(
SELECT
#Group.AccountGroupId AS GroupId,
GroupName AS [Group],
'' AS Account,
SUM( Payment ) AS TotalPayment,
0 AS InnerOrder
FROM
#PaymentTable,
#Group
WHERE
#Group.AccountGroupId = #PaymentTable.AccountGroupId
GROUP BY
#Group.AccountGroupId,
#Group.GroupName
UNION
SELECT
AccountGroupId AS GroupId,
'' AS [Group],
AccountName AS Account,
SUM( Payment ) AS TotalPayment,
1 AS InnerOrder
FROM
#PaymentTable,
#Account
WHERE
#Account.AccountId = #PaymentTable.AccountId
GROUP BY
AccountGroupId,
AccountName
) AS inner_query
ORDER BY
GroupId,
InnerOrder,
Account

Related

Having Trouble Summing distinct values

I have a fairly poorly designed DB that I'm trying to pull reports from. I'm attempting to sum the value on the column GuestCount, however with the structure of the joins, i'm getting a cartesian situation that's making the sum inaccurate. I can't use Sum(Distinct) because I'm not trying to sum the distinct values in GuestCount, but rather the sum of distinct rows.
Here's the SQL to set up the Tables:
CREATE TABLE TesttblTransactions (
ID int,
[sysdate] date,
TxnHour tinyint,
Facility nvarchar(50),
TableID int,
[Check] int,
Item int,
Parent int
)
Create Table TesttblTablesGuests (
ID int,
Facility nvarchar(50),
TableID int,
GuestCount tinyint,
TableDate Date
)
Create Table TesttblFacilities (
ID int,
ClientKey nvarchar(50),
Brand nvarchar(50),
OrgFacilityID nvarchar(50),
UnitID smallint
)
INSERT INTO testtbltransactions (
ID,
[Sysdate],
TxnHour,
Facility,
TableID,
[Check],
Item,
Parent
)
VALUES
(
1,
'20221201',
7,
'JOES',
1001,
12345,
8898989,
0
),
(
2,
'20221201',
7,
'JOES',
1001,
12345,
8776767,
1
),
(
3,
'20221201',
7,
'JOES',
1001,
12345,
856643,
0
),
(
4,
'20221201',
7,
'THE DIVE',
1001,
67890,
662342,
0
),
(
5,
'20221201',
7,
'THE DIVE',
1001,
67890,
244234,
0
),
(
6,
'20221201',
7,
'JOES',
1002,
12344,
873323,
0
);
INSERT INTO testtblTablesGuests (
ID,
Facility,
TableID,
GuestCount,
TableDate
)
VALUES
(
1,
'JOES',
1001,
4,
'20221201'
),
(
2,
'THE DIVE',
1001,
1,
'20221201'
),
(
3,
'JOES',
1002,
1,
'20221201'
);
INSERT INTO testtblFacilities (
ID,
ClientKey,
Brand,
OrgFacilityID,
UnitID
)
VALUES
(
1,
'JOES',
'Joes Hospitality Group LLC',
'Joes Bar',
987
),
(
2,
'THE DIVE',
'The Dive Restaurant Group',
'The Dive',
565
);
--Here's the SQL that I need for reporting but can't seem to get working:
Declare #StartDate as Date = '12-1-2022'
Declare #EndDate as Date = '12-1-2022'
--The query we want to work
SELECT
TesttblFacilities.ClientKey,
TesttblFacilities.Brand,
format(testtbltransactions.sysdate,'yyyy-MM-dd') AS [Date],
'H' AS Freqency,
Testtbltransactions.[TxnHour] AS [Hour],
TesttblFacilities.UnitID AS [UnitID],
'Dine In Guest Count' as Metric,
Sum(TesttblTablesGuests.GuestCount) AS [Value]
FROM ((Testtbltransactions
JOIN Testtbltablesguests ON (Testtbltablesguests.TableDate = Testtbltransactions.sysdate) AND (Testtbltransactions.FACILITY = Testtbltablesguests.facility) AND (Testtbltransactions.tableid = Testtbltablesguests.tableid))
JOIN TesttblFacilities ON Testtbltransactions.FACILITY = TesttblFacilities.ClientKey)
Where (((Testtbltransactions.parent)=0))
and Testtbltransactions.sysdate >= #StartDate
and Testtbltransactions.sysdate <= #EndDate
GROUP BY TesttblFacilities.ClientKey, Testtblfacilities.UnitID,TesttblFacilities.Brand, Testtbltransactions.facility, Testtbltransactions.sysdate, Testtbltransactions.TxnHour`
I'm getting 9 and 2, instead of 5 and 1.
In the comments NBK suggested doing a subquery - and it took me a while but I think i found something that works.. . .
Declare #StartDate as Date = '12-1-2022'
Declare #EndDate as Date = '12-1-2022'
Select
t1.txnhour,
t1.facility,
SUM(t1.guestcount) from
(
Select Distinct
TesttblTransactions.TableID as TableID,
testtbltransactions.[txnHour] as txnhour,
testtbltransactions.Facility as Facility,
testtbltablesguests.GuestCount as guestcount,
testtbltransactions.Parent as parent
From TesttblTransactions
Join TesttblTablesGuests on TesttblTablesGuests.TableID = TesttblTransactions.TableID and testtbltablesguests.Facility = TesttblTransactions.Facility
Where (((Testtbltransactions.parent)=0))
and Testtbltransactions.sysdate >= #StartDate
and Testtbltransactions.sysdate <= #EndDate
) T1
Group by t1.Facility, t1.txnhour, t1.Facility
I'm going to continue to refine this, but I think I should be able to move forward with this.

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

How to get anchor details when using CTE?

I have a table with 2 columns (id, childId) with the following data:
1, 2
2, 4
3, 5
4, 6
5, null
6, null
I have the following CTE that gets the records and it works fine.
DECLARE #id TABLE (id int, childId int);
INSERT INTO #id SELECT 1, 2;
INSERT INTO #id SELECT 2, 4;
INSERT INTO #id SELECT 3, 5;
INSERT INTO #id SELECT 4, 6;
INSERT INTO #id SELECT 5, null;
INSERT INTO #id SELECT 6, null;
WITH cte AS
(
SELECT id, childId
FROM #id
WHERE id = 1
UNION ALL
SELECT b.id, b.childId
FROM #id b
INNER JOIN cte
ON b.id = cte.childId
)
SELECT * FROM cte
However, I would like to add the anchor details so that the results will look like:
1, 2, null
2, 4, 1
4, 6, 1
6, null, 1
So that that 3rd column is the main anchor record.
Is this possible?
Just add a thrid column in CTE.
declare #mytable table ( id int, childId int );
insert #mytable
( id, childId )
values ( 1, 2 ),
( 2, 4 ),
( 3, 5 ),
( 4, 6 ),
( 5, null ),
( 6, null );
declare #id table ( id int );
insert into #id
select 1;
insert into #id
select 3;
with cte
as ( select id ,
childId ,
id root
from #mytable
where id in ( select id
from #id )
union all
select b.id ,
b.childId ,
cte.root
from #mytable b
inner join cte on b.id = cte.childId
)
select *
from cte;
WITH cte AS
(
SELECT id, childId, id AS anchorId
FROM mytable
WHERE
id IN (SELECT id FROM #id)
UNION ALL
SELECT b.id, b.childId, cte.anchorId
FROM mytable b
INNER JOIN cte
ON b.id = cte.childId
)
SELECT
id
, childId
, case
WHEN id = anchorId THEN NULL
ELSE anchorId
END as anchorId
FROM cte
With test code provided:
DECLARE #id TABLE (id int, childId int);
INSERT INTO #id SELECT 1, 2;
INSERT INTO #id SELECT 2, 4;
INSERT INTO #id SELECT 3, 5;
INSERT INTO #id SELECT 4, 6;
INSERT INTO #id SELECT 5, null;
INSERT INTO #id SELECT 6, null;
WITH cte AS
(
SELECT id, childId, id AS anchorId
FROM #id
WHERE id IN (1,3)
UNION ALL
SELECT b.id, b.childId, cte.anchorId
FROM #id b
INNER JOIN cte
ON b.id = cte.childId
)
SELECT
id
, childId
, CASE
WHEN id = anchorId THEN NULL
ELSE anchorId
END AS anchorId
FROM cte

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

Generate sequence number comparing adjacent rows in TSQL

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