Join and flatten table output - sql

I am currently trying to join 3 tables. The main table is company, if there is 3 companies and 2 roles exists in the role table I want the output to be like this:
Company1, Role 1, <iduser>
Company1, Role 2, <iduser>
Company2, Role 1, <iduser>
Company2, Role 2, <iduser>
Company3, Role 1, <iduser>
Company3, Role 2, <iduser>
For each role there must be a row for a company. If no user is linked to a role and company then it should just show: Companyname, Rolename, null
Database schema:
CREATE TABLE [dbo].[role](
[idrole] [int] IDENTITY(1,1) NOT NULL,
[name] [nchar](100) NOT NULL,
CONSTRAINT [PK_role] PRIMARY KEY CLUSTERED
(
[idrole] 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
CREATE TABLE [dbo].[company](
[idcompany] [int] IDENTITY(1,1) NOT NULL,
[name] [nchar](100) NOT NULL,
CONSTRAINT [PK_company] PRIMARY KEY CLUSTERED
(
[idcompany] 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
CREATE TABLE [dbo].[user](
[iduser] [int] IDENTITY(1,1) NOT NULL,
[name] [nchar](100) NOT NULL,
[idcompany] [int] NOT NULL,
[idrole] [int] NULL,
CONSTRAINT [PK_user] PRIMARY KEY CLUSTERED
(
[iduser] 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
ALTER TABLE [dbo].[user] WITH CHECK ADD CONSTRAINT [FK_user_company] FOREIGN KEY([idcompany])
REFERENCES [dbo].[company] ([idcompany])
GO
ALTER TABLE [dbo].[user] CHECK CONSTRAINT [FK_user_company]
GO
ALTER TABLE [dbo].[user] WITH CHECK ADD CONSTRAINT [FK_user_role] FOREIGN KEY([idrole])
REFERENCES [dbo].[role] ([idrole])
GO
ALTER TABLE [dbo].[user] CHECK CONSTRAINT [FK_user_role]
GO
Test data script:
INSERT INTO [test].[dbo].[company]
VALUES ('Company 1')
INSERT INTO [test].[dbo].[company]
VALUES ('Company 2')
INSERT INTO [test].[dbo].[company]
VALUES ('Company 3')
INSERT INTO [test].[dbo].[role]
VALUES ('Role 1')
INSERT INTO [test].[dbo].[role]
VALUES ('Role 2')
Attempt:
SELECT roles.name, users.iduser
FROM [test].[dbo].[role] roles
JOIN [test].[dbo].[user] users ON roles.idrole = users.idrole
JOIN [test].[dbo].[company] company ON company.idcompany = users.idcompany
Please advise what approach I can use to achieve this

Given you don't have a relationship between a Role and a Company, I assume you want every combination of Role/Company, which is where you use a CROSS JOIN.
Then you would LEFT JOIN your User table on since you want to return null if a user doesn't exist for a given Company-Role.
SELECT C.[name], R.[name], U.iduser
FROM [test].[dbo].[role] R
CROSS JOIN [test].[dbo].[company] C
LEFT JOIN [test].[dbo].[user] U ON U.idrole = R.idrole and U.idcompany = C.idcompany
ORDER BY C.[name], R.[name], U.iduser;
Note the use of shorter aliases for clarity.

I think we need to apply a cross join of the 2 tables the Company and Role tables, afterwards join with the users
select * from (
select idcompany, b.name as coname, a.name, a.idrole
from company b
cross join
(select idrole, name from role) a
) c
left join dbuser d on c.idcompany = d.idrole
order by c.coname, c.name

Related

Offset/Fetch Query Slow

I have a SQL Server query that is performing poorly when retrieving data via pagination using offset/fetch. The earlier pages return results very fast but later ones are extremely slow and creating a bottleneck in our system. It's joining on two temp tables (#A and #T). Here is a pared down version of the query:
Select
I.CustomerID,
I.InvoiceID,
I.ItemID,
TI1.TrackID as TrackID1,
TI1.ItemName as ItemName1,
TI1.TrackCatID as TrackCatID1,
TI1.CategoryName as CategoryName1,
TI2.TrackID as TrackID2,
TI2.ItemName as ItemName2,
TI2.TrackCatID as TrackCatID2,
TI2.CategoryName as CategoryName2,
A.AccID,
A.Name as AccountName,
A.utimestamp as UpdateTimeStamp
FROM
#A A
Inner Join [dbo].[Item] I WITH(FORCESEEK)
On
A.CustomerID = I.CustomerID And
A.AccountID = I.AccountID
Left Join #T TI1 On
I.CustomerID = TI1.CustomerID And
I.TrackID1 = TI1.TrackID
Left Join #T TI2 On
I.CustomerID = TI2.CustomerID And
I.TrackID2 = TI2.TrackID
Order by
A.utimestamp
Offset 0 Rows Fetch Next 1000 Rows Only
Where the temp tables are defined as:
Create table #T (CustomerID uniqueidentifier, TrackCatID uniqueidentifier, TrackID uniqueidentifier, ItemName varchar(100), CategoryName varchar(100),PRIMARY KEY (CustomerID,TrackID))
Create table #A (CustomerID uniqueidentifier, AccountID uniqueidentifier, Name varchar(100), utimestamp timestamp, PRIMARY KEY (CustomerID, AccountID))
Regarding the DB table [dbo].[Item] it is defined as:
CREATE TABLE [dbo].[Item](
[Sequence] [int] IDENTITY(1,1) NOT NULL,
[CustomerID] [uniqueidentifier] NOT NULL,
[ItemID] [uniqueidentifier] NOT NULL,
[AccountID] [uniqueidentifier] NULL,
[TrackID1] [uniqueidentifier] NULL,
[TrackID2] [uniqueidentifier] NULL,
CONSTRAINT [PK_Item] PRIMARY KEY NONCLUSTERED
(
[CustomerID] ASC,
[ItemID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 80) ON [PRIMARY],
CONSTRAINT [CX_Item] UNIQUE CLUSTERED
(
[CustomerID] ASC,
[Sequence] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 80) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
And has a number of indexes that each have have several columns:
Item Indexes
1: CusotmerID, Sequence
2: CusotmerID, AccountId, Sequence
3: CusotmerID, TrackId1
4: CusotmerID, TrackId2
5: CusotmerID, ItemID
Is there something I'm missing that's causing the later paginated queries to be slow? Note: The 0 in "Offset 0 Rows" increases by 1000 for every page.
Also, I added a index to the temp table #A and didn't see an improvement to the results:
CREATE INDEX IDX_Timestamp ON #A(utimestamp)

Figure Out Which OrderIDs are 0$ Payment Totals

I am in need to some help writing a SQL 2012 query that will help me find and mark orderID's that are a $0.00 payments due to reversal(s)
So far I have:
Select Distinct a.orderID, a.orderPaid,
(Select SUM((c1.linePrice + c1.lineShippingCost + c1.lineTaxCost + c1.lineOptionCost) * c1.lineQuantity)
From vwSelectOrderLineItems c1 Where c1.orderID = a.orderID) As OrderAmount,
(Select SUM(b1.payAmount) FROM vwSelectOrderPayments b1 Where b1.orderID = a.orderID) as Payment,
1 As IsReversal
From vwSelectOrders a
Left Outer Join vwSelectOrderPayments b On b.orderID = a.orderID
Where b.payValid = 1 AND a.orderPaid = 0
Which is returning me some $0 payments on some orders. When I query that payment table with the orderID of these records, I can see that 2 payments were posted... 1 the original payment, 2 the reversal.
How Can I flag the Orders that are $0 payments?
Oders
CREATE TABLE [dbo].[TblOrders](
[orderID] [bigint] IDENTITY(1000,1) NOT NULL,
[orderPaid] [bit] NOT NULL,
[orderPaidOn] [datetime] NULL
CONSTRAINT [PK_TblOrders] PRIMARY KEY CLUSTERED
(
[orderID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 50) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[TblOrders] ADD CONSTRAINT [DF__TblOrders__order__1975C517] DEFAULT ((0)) FOR [orderPaid]
Order Line Items
CREATE TABLE [dbo].[TblOrderLineItems](
[lineID] [bigint] IDENTITY(1,1) NOT NULL,
[orderID] [bigint] NOT NULL,
[lineQuantity] [int] NOT NULL,
[linePrice] [money] NOT NULL,
[lineShippingCost] [money] NOT NULL,
[lineTaxCost] [money] NOT NULL,
[lineOptionCost] [money] NOT NULL,
CONSTRAINT [PK_TblOrderLineItems] PRIMARY KEY CLUSTERED
(
[lineID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 50) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[TblOrderLineItems] ADD CONSTRAINT [DF_TblOrderLineItems_lineShippingCost] DEFAULT ((0)) FOR [lineShippingCost]
GO
ALTER TABLE [dbo].[TblOrderLineItems] ADD CONSTRAINT [DF_TblOrderLineItems_lineTaxCost] DEFAULT ((0)) FOR [lineTaxCost]
GO
ALTER TABLE [dbo].[TblOrderLineItems] ADD CONSTRAINT [DF_TblOrderLineItems_lineOptionCost] DEFAULT ((0)) FOR [lineOptionCost]
GO
Order Payments
CREATE TABLE [dbo].[TblOrderPayments](
[paymentID] [bigint] IDENTITY(1,1) NOT NULL,
[orderID] [bigint] NOT NULL,
[payAmount] [money] NOT NULL,
[payPosted] [datetime] NOT NULL,
[payValid] [bit] NOT NULL,
CONSTRAINT [PK_TblOrderPayments] PRIMARY KEY CLUSTERED
(
[paymentID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 50) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[TblOrderPayments] ADD CONSTRAINT [DF_TblOrderPayments_payValid] DEFAULT ((0)) FOR [payValid]
GO
Views
CREATE VIEW [dbo].[vwSelectOrderLineItems] AS
SELECT linePrice, lineShippingCost, lineTaxCost, lineOptionCost, lineQuantity
FROM [dbo].[TblOrderLineItems]
CREATE VIEW [dbo].[vwSelectOrderPayments] AS
SELECT paymentID, orderID, payAmount, payValid
FROM dbo.TblOrderPayments
CREATE VIEW [dbo].[vwSelectOrders] AS
SELECT orderID , orderPaid
FROM dbo.TblOrders
Note
I cannot change the table structure
SELECT distinct a.orderid,
a.orderPaid,
c.OrderAmount
d.Payment
From vwSelectOrders AS a
INNER JOIN ( Select SUM((linePrice + lineShippingCost + lineTaxCost + lineOptionCost) * lineQuantity) As orderAmount,OrderID
From vwSelectOrderLineItems group by orderid) AS C on c.orderID = a.orderID
INNER JOIN (Select SUM(payAmount) as Payment,orderID FROM vwSelectOrderPayments WHERE isnull(SUM(PayAmount),0) > 0 GROUP BY OrderID) AS d ON d.orderID = a.orderID
Left Outer Join vwSelectOrderPayments b On b.orderID = a.orderID
Where b.payValid = 1 AND a.orderPaid = 0 AND
This is a better query as you do not have to us a correlated subquery. Correlated queries are when a subquery references an outerquery row. This isn't optimal because every row the outerquery runs the correlated subquery will execute. Once you give us table definitions we can probably fix the overall data return of your query.

Select count from another table to each row in result rows

Here are the tables:
CREATE TABLE [dbo].[Classes](
[ClassId] [int] NOT NULL,
[ClassName] [nvarchar](50) NOT NULL,
CONSTRAINT [PK_Classes] PRIMARY KEY CLUSTERED
(
[ClassId] 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
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Students](
[StudentId] [int] NOT NULL,
[ClassId] [int] NOT NULL,
CONSTRAINT [PK_Students] PRIMARY KEY CLUSTERED
(
[StudentId] 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
ALTER TABLE [dbo].[Students] WITH CHECK ADD CONSTRAINT [FK_Students_Classes] FOREIGN KEY([ClassId])
REFERENCES [dbo].[Classes] ([ClassId])
GO
ALTER TABLE [dbo].[Students] CHECK CONSTRAINT [FK_Students_Classes]
GO
I want to get list of class, and each class - the number of student which belong to each class.
How can I do this?
You need to do this -
SELECT C.ClassId, C.ClassName, count(S.StudentId) AS studentCount
FROM CLASSES C LEFT JOIN STUDENTS S ON (C.ClassId=S.ClassId)
GROUP BY C.ClassId, C.ClassName
You mean something like this?
SELECT C.[ClassName], COUNT(*) AS 'Number of Students'
FROM [dbo].[Classes] AS C
INNER JOIN [dbo].[Students] AS S ON S.[ClassId] = C.[ClassId]
GROUP BY C.[ClassName]
Without having to add the group by clause, you can do the following:
Create a function to get the students count:
go
CREATE FUNCTION [dbo].GetStudentsCountByClass(#classId int) RETURNS INT
AS BEGIN
declare #count as int
select #count = count(*) from STUDENTS
where ClassId = #classId
RETURN #count
END
then use it in your select statement
SELECT * , dbo.GetStudentsCountByClass(ClassId) AS StudentsCount
FROM Classes
select c.ClassId,C.ClassName,COUNT(*) [Number of students]
from Classes C,Students S
where c.ClassId=S.ClassId
group by C.ClassId,C.ClassName
SELECT class.ClassId, count(student .StudentId) AS studentCount
FROM dbo.CLASSES class LEFT JOIN dbo.STUDENTS student ON (class.ClassId=student.ClassId)
GROUP BY class.ClassId

how to select parent row only if has at least one child with using two tables

Hello I have tables sctucture with datas and 2 small questions.
CREATE TABLE [dbo].[Parent] (
[id] [int] NOT NULL,
[Name] [nvarchar](50) NOT NULL,
CONSTRAINT [PK_Parent] 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]
GO
CREATE TABLE [dbo].[Child1] (
[Child1Id] [int] NOT NULL,
[ParentId] [int] NOT NULL,
[SomeData] [int] NOT NULL,
CONSTRAINT [PK_Child1] PRIMARY KEY CLUSTERED
(
[Child1Id] 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
CREATE TABLE [dbo].[Child2] (
[Child2Id] [int] NOT NULL,
[ParentId] [int] NOT NULL,
[SomeData] [int] NOT NULL,
CONSTRAINT [PK_Child2] PRIMARY KEY CLUSTERED
(
[Child2Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
INSERT INTO Parent VALUES(1,'Name 1')
INSERT INTO Parent VALUES(2,'Name 2')
INSERT INTO Parent VALUES(3,'Name 3')
INSERT INTO Parent VALUES(4,'Name 4')
INSERT INTO [Child1] VALUES(1,1,50)
INSERT INTO [Child1] VALUES(2,1,125)
INSERT INTO [Child1] VALUES(3,2,255)
INSERT INTO [Child2] VALUES(1,1,2)
INSERT INTO [Child2] VALUES(2,2,4)
INSERT INTO [Child2] VALUES(3,2,8)
INSERT INTO [Child2] VALUES(4,3,16)
How select all parets record with at least one child type in both tables.
I did next query
but I don't know optimal way to show total count of this records
SELECT p.Name, count(Child1) , count(Child2)
How select all parets records only that exist in both tables ?
SELECT p.Name, count(Child1) , count(Child2)
Thanks in Advice.
Query 1:
--at least one child record in either Child1 or Child2
select distinct p.*
from parent p
left outer join child1 c1 on p.id = c1.ParentId
left outer join child2 c2 on p.id = c2.ParentId
where coalesce(c1.ParentId, c2.ParentId) is not null
Query 2:
--at least one child record in both Child1 and Child2
select distinct p.*
from parent p
inner join child1 c1 on p.id = c1.ParentId
inner join child2 c2 on p.id = c2.ParentId
Note: If you just want to show the count of parent records, change
select distinct p.*
to
select count(distinct p.id)
in either query.

How should I migrate this data into these Sql Server tables?

I wish to migrate some data from a single table into these new THREE tables.
Here's my destination schema:
Notice that I need to insert into the first Location table .. grab the SCOPE_IDENTITY() .. then insert the rows into the Boundary and Country tables.
The SCOPE_IDENTITY() is killing me :( meaning, I can only see a way to do this via CURSORS. Is there a better alternative?
UPDATE
Here's the scripts for the DB Schema....
Location
CREATE TABLE [dbo].[Locations](
[LocationId] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](100) NOT NULL,
[OriginalLocationId] [int] NOT NULL,
CONSTRAINT [PK_Locations] PRIMARY KEY CLUSTERED
(
[LocationId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
)
Country
CREATE TABLE [dbo].[Locations_Country](
[IsoCode] [nchar](2) NOT NULL,
[LocationId] [int] NOT NULL,
CONSTRAINT [PK_Locations_Country] PRIMARY KEY CLUSTERED
(
[LocationId] 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
ALTER TABLE [dbo].[Locations_Country] WITH CHECK ADD CONSTRAINT [FK_Country_inherits_Location] FOREIGN KEY([LocationId])
REFERENCES [dbo].[Locations] ([LocationId])
GO
ALTER TABLE [dbo].[Locations_Country] CHECK CONSTRAINT [FK_Country_inherits_Location]
GO
Boundary
CREATE TABLE [dbo].[Boundaries](
[LocationId] [int] NOT NULL,
[CentrePoint] [varbinary](max) NOT NULL,
[OriginalBoundary] [varbinary](max) NULL,
[LargeReducedBoundary] [varbinary](max) NULL,
[MediumReducedBoundary] [varbinary](max) NULL,
[SmallReducedBoundary] [varbinary](max) NULL,
CONSTRAINT [PK_Boundaries] PRIMARY KEY CLUSTERED
(
[LocationId] 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
SET ANSI_PADDING OFF
GO
ALTER TABLE [dbo].[Boundaries] WITH CHECK ADD CONSTRAINT [FK_LocationBoundary] FOREIGN KEY([LocationId])
REFERENCES [dbo].[Locations] ([LocationId])
GO
ALTER TABLE [dbo].[Boundaries] CHECK CONSTRAINT [FK_LocationBoundary]
GO
I don't see a need for SCOPE_IDENTITY or cursors if you approach the data in order of the parent/child relationship:
INSERT INTO LOCATION
SELECT t.name,
t.originallocationid
FROM ORIGINAL_TABLE t
GROUP BY t.name, t.originallocationid
INSERT INTO COUNTRY
SELECT DISTINCT
t.isocode,
l.locationid
FROM ORIGINAL_TABLE t
JOIN LOCATION l ON l.name = t.name
AND l.originallocationid = t.originalocationid
INSERT INTO BOUNDARY
SELECT DISTINCT
l.locationid,
t.centrepoint,
t.originalboundary,
t.largereducedboundary,
t.mediumreducedboundary,
t.smallreducedboundary
FROM ORIGINAL_TABLE t
JOIN LOCATION l ON l.name = t.name
AND l.originallocationid = t.originalocationid
After loading your Location table you could create a query that joins Location with your source single table. The join criteria would be the natural key (is that the Name column?) and it would return the new LocationId along with the Boundary data. The results would inserted into the new Boundary table.