SQL Recursive Count - sql

I have two tables I am joining with the following structure:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[ContentDivider](
[Id] [int] IDENTITY(1,1) NOT NULL,
[ParentId] [int] NULL,
[Name] [nvarchar](128) NOT NULL,
CONSTRAINT [PK_ContentDivider] 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
/****** Object: Table [dbo].[CustomPage] Script Date: 23-03-2020 17:46:09 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[CustomPage](
[Id] [int] IDENTITY(1,1) NOT NULL,
[ContentDividerId] [int] NOT NULL,
[Name] [nvarchar](128) NOT NULL,
CONSTRAINT [PK_CustomPage] 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
A ContentDivider can have n ContentDividers as Children and can have m CustomPages as children as well.
I want a View that counts the Display the current CustomDivider and the COunt for all the CustomPages as Children of the current ContentDivider.
My Test data:
SET IDENTITY_INSERT [dbo].[ContentDivider] ON
GO
INSERT [dbo].[ContentDivider] ([Id], [ParentId], [Name]) VALUES (1, NULL, N'TopLevel1')
INSERT [dbo].[ContentDivider] ([Id], [ParentId], [Name]) VALUES (2, NULL, N'TopLevel2')
INSERT [dbo].[ContentDivider] ([Id], [ParentId], [Name]) VALUES (3, NULL, N'TopLevel3')
INSERT [dbo].[ContentDivider] ([Id], [ParentId], [Name]) VALUES (4, 1, N'SecondLevel1')
INSERT [dbo].[ContentDivider] ([Id], [ParentId], [Name]) VALUES (5, 1, N'SecondLevel2')
INSERT [dbo].[ContentDivider] ([Id], [ParentId], [Name]) VALUES (6, 1, N'SecondLevel3')
INSERT [dbo].[ContentDivider] ([Id], [ParentId], [Name]) VALUES (7, 4, N'ThirdLevel1')
INSERT [dbo].[ContentDivider] ([Id], [ParentId], [Name]) VALUES (8, 4, N'ThirdLevel2')
GO
SET IDENTITY_INSERT [dbo].[ContentDivider] OFF
GO
SET IDENTITY_INSERT [dbo].[CustomPage] ON
GO
INSERT [dbo].[CustomPage] ([Id], [ContentDividerId], [Name]) VALUES (1, 1, N'Level1_1')
INSERT [dbo].[CustomPage] ([Id], [ContentDividerId], [Name]) VALUES (2, 1, N'Level1_2')
INSERT [dbo].[CustomPage] ([Id], [ContentDividerId], [Name]) VALUES (3, 2, N'Level1_3')
INSERT [dbo].[CustomPage] ([Id], [ContentDividerId], [Name]) VALUES (4, 2, N'Level1_4')
INSERT [dbo].[CustomPage] ([Id], [ContentDividerId], [Name]) VALUES (5, 4, N'Level1_5')
INSERT [dbo].[CustomPage] ([Id], [ContentDividerId], [Name]) VALUES (6, 5, N'Level1_6')
INSERT [dbo].[CustomPage] ([Id], [ContentDividerId], [Name]) VALUES (7, 7, N'Level1_7')
INSERT [dbo].[CustomPage] ([Id], [ContentDividerId], [Name]) VALUES (8, 8, N'Level1_8')
GO
SET IDENTITY_INSERT [dbo].[CustomPage] OFF
GO
And the View I want to extend:
SELECT dbo.ContentDivider.ParentId, dbo.ContentDivider.Name, dbo.ContentDivider.Id, COUNT(DISTINCT dbo.CustomPage.Id) AS CustomPageCount
FROM dbo.ContentDivider LEFT OUTER JOIN
dbo.CustomPage ON dbo.ContentDivider.Id = dbo.CustomPage.ContentDividerId
GROUP BY dbo.ContentDivider.ParentId, dbo.ContentDivider.Name, dbo.ContentDivider.Id
As for now the view counts the custompages directly underneath the contentdivider. I would like all the CustomPages as children counted.
Any suggestions?
The respected result would be:
View

this sounds like a perfect situation for recursive cte ;)
So, if I understood correctly, your expected result would be Toplevel1 with 6 pages and Toplevel 2 with 2 pages since all the other levels are somewhere beneath these two mentioned levels?
The cte might look something like this (maybe you habe to include the max recursion option):
WITH cte AS(
SELECT 1 lvl, ID AS ParentID, ID, Name
FROM dbo.ContentDivider cd
WHERE ParentId IS NULL
UNION ALL
SELECT c.lvl+1 AS lvl, c.ParentID, cd.ID, cd.Name
FROM dbo.ContentDivider cd
INNER JOIN cte c ON cd.ParentID = c.ID
)
SELECT c.ParentID, cd.Name, COUNT(DISTINCT cp.Id) AS CustomPageCount
FROM cte c
JOIN dbo.ContentDivider cd ON cd.ID = c.ParentID
LEFT OUTER JOIN dbo.CustomPage cp ON cp.ContentDividerId = c.id
GROUP BY c.ParentId, cd.Name
This leads to all pages being assigned to its top level.
See fiddle for details: http://sqlfiddle.com/#!18/f1a44/28/1
Edit: Since you need the details down to DividerID, I extended my example in the fiddle. First of all, I fetch the PageCount per ID in one cte and additionally the PageCount aggregated on level (ParentID and all its IDs) - this done you don't need the count and grouping in the following ctes.
In my query I then check, if my current rows ID is a top-level of any kind and assign the corresponding PageCount to this row.
WITH cteCnt AS(
SELECT cd.ID, COUNT(DISTINCT cp.Id) AS CustomPageCount
FROM dbo.ContentDivider cd
LEFT OUTER JOIN dbo.CustomPage cp ON cp.ContentDividerId = cd.id
GROUP BY cd.ID
),
cteTop AS(
SELECT cd.ID, COUNT(DISTINCT cp.Id) AS CustomPageCount
FROM dbo.ContentDivider cd
LEFT OUTER JOIN dbo.CustomPage cp ON cp.ContentDividerId = cd.id
GROUP BY cd.ID
UNION ALL
SELECT cd.ParentID, COUNT(DISTINCT cp.Id) AS CustomPageCount
FROM dbo.ContentDivider cd
LEFT OUTER JOIN dbo.CustomPage cp ON cp.ContentDividerId = cd.id
WHERE cd.ParentID IS NOT NULL
GROUP BY cd.ParentID
),
cteTopSum AS(
SELECT ID, SUM(CustomPageCount) AS CustomPageCount
FROM ctetop
GROUP BY ID
),
cte AS(
SELECT 1 lvl, cd.ID AS ParentID, cd.ID AS ParentIDx, cd.ID, cd.Name, cnt.CustomPageCount
FROM dbo.ContentDivider cd
INNER JOIN cteCnt cnt ON cnt.ID = cd.ID
WHERE ParentId IS NULL
UNION ALL
SELECT c.lvl+1 AS lvl, c.ParentID, cd.ParentID AS ParentIDx, cd.ID, cd.Name, cnt.CustomPageCount
FROM dbo.ContentDivider cd
INNER JOIN cteCnt cnt ON cnt.ID = cd.ID
INNER JOIN cte c ON cd.ParentID = c.ID
),
cteOut AS(
SELECT *
,SUM(CustomPageCount) OVER (PARTITION BY ParentID) x
,SUM(CustomPageCount) OVER (PARTITION BY ParentIDx) y
FROM cte c
)
SELECT CASE WHEN co.ParentIDx = co.ID THEN NULL ELSE co.ParentIDx END AS ParentID, co.ID, co.Name, CASE WHEN co.ID = co.ParentID THEN co.X ELSE ts.CustomPageCount END CustomPageCount
FROM cteOut co
LEFT JOIN cteTopSum ts ON ts.ID = co.ID
ORDER BY 1, 2
See new fiddle for details: http://sqlfiddle.com/#!18/f1a44/185/1
I'm mot sure, if there is a prettier / nicer way to solve this, but seemingly this seems to solve the problem.
However, I did NOT check if it works if any number of sublevels or whatsoever - if you find any issues, feel free to comment.

Related

How to return "most populated","least populated" countries grouped by continent organized in a single table via SQL?

This is a variant of the SQLzoo tutorial.
'world' table contains fields
'population'(assigned to each country),
'name' (all countries) and
'continent' (assigned to each country).
Expected output is a table as shown below
Continent
Most_populous
Least_populous
Africa
Ghana
xyz
Asia
China
abc
I did try a complicated function as below, but was not able to get it to work due to "SQL error". Not sure why.
SELECT DISTINCT continent
, (SELECT x.name
FROM world x
WHERE x.population = (SELECT max(y.population)
FROM world y
WHERE x.continent = y.continent)) AS most_populous
, (SELECT z.name
FROM world z
WHERE z.population = (SELECT min(a.population)
FROM world a
WHERE a.continent=z.continent)) AS least_populous FROM world;
Is there an easier way to get the required output?
You can try this:
SELECT world.continent,
(SELECT x.name
FROM world x
WHERE x.population = (SELECT max(y.population)
FROM world y
WHERE x.continent = y.continent)
AND world.continent = x.continent
) AS most_populous,
(SELECT z.name
FROM world z
WHERE z.population = (SELECT min(a.population)
FROM world a
WHERE z.continent = a.continent)
AND world.continent = z.continent
) AS least_populous
FROM world
GROUP BY world.continent;
Thank you
Since I did not had the tables with me, I ended up creating one. It would be better if you could include table and data creation scripts in question. Second, this is going to be a fairly small table so I am not going to worry about performance. I would create a view based on this query and that will be good enough.
Table creation scripts:
USE [StackOverflow]
GO
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[CountryPopulation]') AND type in (N'U'))
DROP TABLE [dbo].[CountryPopulation]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[CountryPopulation](
[ID] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](max) NULL,
[Continent] [nvarchar](max) NULL,
[Population] [int] NULL,
CONSTRAINT [PK_CountryPopulation] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
USE [StackOverflow]
GO
SET IDENTITY_INSERT [dbo].[CountryPopulation] ON
GO
INSERT [dbo].[CountryPopulation] ([ID], [Name], [Continent], [Population]) VALUES (1, N'C1', N'Asia', 100)
GO
INSERT [dbo].[CountryPopulation] ([ID], [Name], [Continent], [Population]) VALUES (2, N'C2', N'Asia', 200)
GO
INSERT [dbo].[CountryPopulation] ([ID], [Name], [Continent], [Population]) VALUES (3, N'C3', N'Asia', 300)
GO
INSERT [dbo].[CountryPopulation] ([ID], [Name], [Continent], [Population]) VALUES (4, N'C4', N'Europe', 100)
GO
INSERT [dbo].[CountryPopulation] ([ID], [Name], [Continent], [Population]) VALUES (5, N'C5', N'Europe', 200)
GO
INSERT [dbo].[CountryPopulation] ([ID], [Name], [Continent], [Population]) VALUES (6, N'C6', N'Europe', 300)
GO
INSERT [dbo].[CountryPopulation] ([ID], [Name], [Continent], [Population]) VALUES (7, N'C7', N'Africa', 100)
GO
INSERT [dbo].[CountryPopulation] ([ID], [Name], [Continent], [Population]) VALUES (8, N'C8', N'Africa', 200)
GO
INSERT [dbo].[CountryPopulation] ([ID], [Name], [Continent], [Population]) VALUES (9, N'C9', N'Africa', 200)
GO
SET IDENTITY_INSERT [dbo].[CountryPopulation] OFF
GO
Query to get result:
SELECT MinMax.Continent,
ForLeast.[name] AS LeastPopulous,
ForMost.[name] AS MostPopulous
FROM CountryPopulation ForMost JOIN
CountryPopulation ForLeast JOIN
( SELECT DISTINCT Continent,
MIN([population]) OVER(PARTITION BY continent) AS LeastPopulation,
MAX([population]) OVER(PARTITION BY continent) AS MaxPopulation
FROM CountryPopulation) MinMax
ON ForLeast.Continent = MinMax.Continent AND ForLeast.[Population] = MinMax.LeastPopulation
ON ForMost.Continent = MinMax.Continent AND ForMost.[Population] = MinMax.MaxPopulation
Note the results for Africa. There are 2 rows. It is possible for more than one country to have least or most population. You would want to think on how to handle that scenario.

SQL tree data rebuild

Good morning,
I have an issue with a SQL query on hierarchic data.
I have to store the position of products on the shelfs in a supermarket.
Products can be moved but I have to maintain the history in order to produce sales statistics.
Records in the table are hierarchically linked and they can change some of their information and the position on the shelf.
Shelf_code: identify the shelf hierarchic position (i.e. aisle, shelf, level, sector…)
Shelf_position: details the location on shelf level.
parentId is set to null for the first insert occurrence of a record and is set to the ancestor record id for the further versions.
I’ve produced a tree structured set of records that can be shown like in the picture.
At the beginning of the year the products are positioned as follows
enter image description here
Then a new brand has been inserted between B and C and C has been moved in a different position (with his children C flavour x and C flavour y)
In the database new records are created for Brand C items with the new shelf code and position, parent id is now not null and corresponds to the id of the original record.
When I rebuild the supermarket shelfs tree, I want to show only the new positions
enter image description here
I tried to get data using partition over Shelf_position but I get twice the records named “Flowour x” and “Flowour y”: both in the correct position but also in the previous one (so they look like being Brand New sons that isn’t correct)
enter image description here
My query:
SELECT Shelfs.*
FROM Shelfs
WHERE Shelfs.ID IN(
SELECT id
FROM
(SELECT m.*, ROW_NUMBER() over
(partition by Shelf_Position order by m.time_stamp desc) as rn
FROM Shelfs m) m2
WHERE m2.rn = 1)
ORDER BY Shelf_Code, Shelf_Position
To get item genealogy of product named “Flavour x” I’ve used the query listed below.
Selecting the most recent item I can get the last one
WITH Anchestors AS (
SELECT Shelfs.*
,0 AS lv
FROM Shelfs
WHERE id = #productId
UNION ALL
SELECT parent.*
, lv+1 as lv
FROM Shelfs parent
INNER JOIN Anchestors a
ON parent.id = a.parentId
)
SELECT #rootId = ( SELECT top(1) id
FROM Anchestors
order by lv desc);
-- select root descendants
WITH generation AS (
SELECT Shelfs.*
,0 AS lv
FROM Shelfs
WHERE parentId IS NULL
and id = #rootId
UNION ALL
SELECT child.*
, lv+1 as lv
FROM Shelfs child
INNER JOIN generation g
ON g.id = child.parentId
)
SELECT *
FROM generation
order by lv
But this query is parametrized for a specific product and I wasn’t able to generalize it in order to get the last version of all the items.
Can anyone help me to find out the correct way to get the data?
Thanks in advance to everybody.
Database table script:
CREATE TABLE [dbo].[Shelfs](
[id] [uniqueidentifier] NOT NULL,
[Name] [nchar](10) NULL,
[Shelf_Code] [nvarchar](max) NULL,
[Shelf_Position] [nvarchar](max) NULL,
[parentId] [uniqueidentifier] NULL,
[time_stamp] [datetime] NULL,
CONSTRAINT [PK_Shelfs] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[Shelfs] ADD CONSTRAINT [DF_Shelfs_id] DEFAULT (newid()) FOR [id]
INSERT [dbo].[Shelfs] ([id], [Name], [Shelf_Code], [Shelf_Position], [parentId], [time_stamp]) VALUES (N'043be568-8cc6-4624-9373-64d31a032745', N'Level 1 ', N'0101', N'010101', NULL, CAST(N'2022-01-01T08:00:00.000' AS DateTime))
INSERT [dbo].[Shelfs] ([id], [Name], [Shelf_Code], [Shelf_Position], [parentId], [time_stamp]) VALUES (N'27ccf84c-5a08-425b-a355-6fb1f99eda3a', N'Flavour x ', N'0101010103', N'010101010301', NULL, CAST(N'2022-01-01T08:00:00.000' AS DateTime))
INSERT [dbo].[Shelfs] ([id], [Name], [Shelf_Code], [Shelf_Position], [parentId], [time_stamp]) VALUES (N'9e841e78-571a-4009-8d40-81fc9045b99e', N'Brand C ', N'01010101', N'0101010104', N'37035b46-6a1a-47b6-8bdc-4a6ecfd68b9e', CAST(N'2022-02-01T08:00:00.000' AS DateTime))
INSERT [dbo].[Shelfs] ([id], [Name], [Shelf_Code], [Shelf_Position], [parentId], [time_stamp]) VALUES (N'da5119e1-c327-463a-b8e9-84453a1495cc', N'2nd Aisle ', N'00', N'02', NULL, CAST(N'2022-01-01T08:00:00.000' AS DateTime))
INSERT [dbo].[Shelfs] ([id], [Name], [Shelf_Code], [Shelf_Position], [parentId], [time_stamp]) VALUES (N'b3e67cd1-c726-4bcf-a30c-9b7945a2ea9f', N'Left ', N'01', N'0101', NULL, CAST(N'2022-01-01T08:00:00.000' AS DateTime))
INSERT [dbo].[Shelfs] ([id], [Name], [Shelf_Code], [Shelf_Position], [parentId], [time_stamp]) VALUES (N'08c2d2bb-0980-4d0d-899d-a4d23c0c7878', N'Flavour x ', N'0101010104', N'010101010401', N'27ccf84c-5a08-425b-a355-6fb1f99eda3a', CAST(N'2022-02-01T08:00:00.000' AS DateTime))
INSERT [dbo].[Shelfs] ([id], [Name], [Shelf_Code], [Shelf_Position], [parentId], [time_stamp]) VALUES (N'56825bd1-55ab-4c65-86c0-aff40550b785', N'Flavour y ', N'0101010103', N'010101010302', NULL, CAST(N'2022-01-01T08:00:00.000' AS DateTime))
INSERT [dbo].[Shelfs] ([id], [Name], [Shelf_Code], [Shelf_Position], [parentId], [time_stamp]) VALUES (N'4a8e4ef7-926c-4eca-a520-b580f7035f44', N'Right ', N'01', N'0102', NULL, CAST(N'2022-01-01T08:00:00.000' AS DateTime))
INSERT [dbo].[Shelfs] ([id], [Name], [Shelf_Code], [Shelf_Position], [parentId], [time_stamp]) VALUES (N'c53d35bc-f31b-4a67-a9e7-b82fbae5f7dc', N'Sector 1 ', N'010101', N'01010101', NULL, CAST(N'2022-01-01T08:00:00.000' AS DateTime))
INSERT [dbo].[Shelfs] ([id], [Name], [Shelf_Code], [Shelf_Position], [parentId], [time_stamp]) VALUES (N'c70a6b81-8967-4ae8-a16c-bc6a925e1f61', N'Flavour y ', N'0101010104', N'010101010402', N'56825bd1-55ab-4c65-86c0-aff40550b785', CAST(N'2022-02-01T08:00:00.000' AS DateTime))
INSERT [dbo].[Shelfs] ([id], [Name], [Shelf_Code], [Shelf_Position], [parentId], [time_stamp]) VALUES (N'c46948f0-2d5e-48cf-a773-dcc44a434779', N'Brand B ', N'01010101', N'0101010102', NULL, CAST(N'2022-01-01T08:00:00.000' AS DateTime))
INSERT [dbo].[Shelfs] ([id], [Name], [Shelf_Code], [Shelf_Position], [parentId], [time_stamp]) VALUES (N'63123c3e-36bf-477e-919b-f245f0ff80c2', N'Sector 2 ', N'010101', N'01010102', NULL, CAST(N'2022-01-01T08:00:00.000' AS DateTime))
I fancy I've found the solution for my issue.
For who has interest in it, here's the query:
WITH ShelfsTree([id]
,[Name]
,[Shelf_Code]
,[Shelf_Position]
,[parentId]
,[Time_stamp]
, lv)
AS (SELECT a.[id],
a.[Name],
a.[Shelf_Code],
a.[Shelf_Position],
a.[parentId] Parent,
a.[Time_stamp],
0 AS lv
FROM Shelfs a
WHERE a.[parentId] IS NULL
AND a.[parentId] IS NULL
UNION ALL
SELECT a2.[id],
a2.[Name],
a2.[Shelf_Code],
a2.[Shelf_Position],
a2.[parentId] Parent,
a2.[Time_stamp],
ShelfsTree.lv + 1 lv
FROM Shelfs a2
INNER JOIN ShelfsTree ON ShelfsTree.[id] = a2.[parentId]
WHERE a2.[parentId] IS NOT NULL
)
select *
from ShelfsTree
where id not in (select [parentId] from Shelfs where [parentId] is not null)
order by Shelf_Code, Shelf_Position, Time_stamp desc
Thanks.

SELECT data from multiple tables using max?

I have Project and Score tables like this
How do I query to get this result, to show all ProjectID, ProjectName, and its newest Score (latest Date) with the Date:
I tried:
SELECT R.ProjectID, Name, Score, Date
FROM PWINProject, PWINRecord R
WHERE Date = (
SELECT max(Date)
FROM PWINRecord
WHERE ProjectID = R.ProjectID
)
AND PWINProject.ProjectID = R.ProjectID
But it only shows me projects with a score, when a project doesn't have a score yet (e.g. #3 - Amazon) it won't show.
You need a left join instead on an inner join (and use a proper join not the non-standard old style join you are using). However it also simplifies the query to use a window function to determine what row to return e.g.
declare #PWINProject table (ProjectId int, [Name] varchar(256))
insert into #PWINProject(ProjectId, [Name])
select 1, 'Database'
union all select 2, 'Microsoft'
union all select 3, 'Amazon'
union all select 4, 'IBM'
declare #PWINRecord table (ScoreId int, ProjectId int, Score int, [Date] datetime)
insert into #PWINRecord (ScoreId, ProjectId, Score, [Date])
select 1, 1, 100, '2019-01-15 19:40:46.723'
union all select 2, 1, 52, '2019-01-15 20:40:46.723'
union all select 3, 2, 60, '2019-01-15 21:40:46.723'
union all select 4, 2, 55, '2019-01-15 22:40:46.723'
union all select 5, 2, 72, '2019-01-15 23:41:46.723'
union all select 6, 4, 111, '2019-01-16 10:40:46.723'
union all select 7, 4, 90, '2019-01-17 12:40:46.723'
select ProjectId, [Name], Score, [Date]
from (
SELECT P.ProjectID, [Name], Score, [Date]
, row_number() over (partition by R.ProjectID order by [Date] desc) Row#
FROM #PWINProject P
left join #PWINRecord R on R.ProjectID = P.ProjectID
) X
where X.Row# = 1
order by ProjectId
Returns:
ProjectID Name Score Date
1 Database 52 2019-01-15 20:40:46.723
2 Microsoft 72 2019-01-15 23:41:46.723
3 Amazon NULL NULL
4 IBM 90 2019-01-17 12:40:46.723
PS: This is the recommended style for posting an SQL question, where you setup the data into temp tables or table variables yourself - saves people answering a lot of time. Data in images is a no-no.
One approach would be to use OUTER APPLY:
DECLARE #Project TABLE (ProjectId INT, ProjectName VARCHAR(100))
INSERT INTO #Project
VALUES (1, 'Database'), (2, 'Microsoft'), (3, 'Amazon'), (4, 'IBM')
DECLARE #Score TABLE(ScoreId INT, ProjectId INT, Score INT, RefDate DATE)
INSERT INTO #Score
VALUES (1, 1, 100, '2019-01-01'), (2, 2, 200, '2019-02-02'), (4, 4, 400, '2019-04-04')
SELECT
P.ProjectId,
P.ProjectName,
S.Score,
S.RefDate
FROM #Project P
OUTER APPLY (
SELECT TOP 1 S.*
FROM #Score S
WHERE S.ProjectId = P.ProjectId ORDER BY S.RefDate DESC
) S
You need to be careful outer apply is not very efficient but it's clean and easy to understand. Other techniques may work involving ROW_NUMBER, you should study a bit the execution plan to see what fits best.
Try this:
select P.ProjectID, P.ProjectName, R.Score, R.Date
from PWINProject P
left outer join PWINRecord R on R.ProjectID = P.ProjectID
and R.Date = (
select max(R2.Date)
from PWINRecord R2
where R2.ProjectID = R.ProjectID
)
A simple solution is the following:
select p.ProjectID, p.ProjectName, s.Score as LatestScore, s.[date]
from Project as p
left outer join Scores as s ON p.ProjectID = s.ProjectID
where (
s.[date] = (
select top (1) s2.[date]
from Scores as s2
where s2.ProjectID = s.ProjectID
order by [date] desc
)
or s.[date] is null
)
CREATE TABLE [dbo].[PWINProject](
[ProjectID] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) NULL,
CONSTRAINT [PK_PWINProject] PRIMARY KEY CLUSTERED
(
[ProjectID] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
CREATE TABLE [dbo].[PWINRecord](
[ScoreId] [int] NOT NULL,
[ProjectID] [int] NULL,
[Score] [int] NULL,
[Date] [datetime] NULL,
CONSTRAINT [PK_PWINRecord] PRIMARY KEY CLUSTERED
(
[ScoreId] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
SET IDENTITY_INSERT [dbo].[PWINProject] ON
INSERT [dbo].[PWINProject] ([ProjectID], [Name]) VALUES (1, N'Database')
INSERT [dbo].[PWINProject] ([ProjectID], [Name]) VALUES (2, N'Microsoft')
INSERT [dbo].[PWINProject] ([ProjectID], [Name]) VALUES (3, N'Amazone')
INSERT [dbo].[PWINProject] ([ProjectID], [Name]) VALUES (4, N'IBM')
SET IDENTITY_INSERT [dbo].[PWINProject] OFF
INSERT [dbo].[PWINRecord] ([ScoreId], [ProjectID], [Score], [Date]) VALUES (1, 1, 100, CAST(N'2019-01-15 00:00:00.000' AS DateTime))
INSERT [dbo].[PWINRecord] ([ScoreId], [ProjectID], [Score], [Date]) VALUES (2, 1, 52, CAST(N'2019-01-15 00:00:00.000' AS DateTime))
INSERT [dbo].[PWINRecord] ([ScoreId], [ProjectID], [Score], [Date]) VALUES (3, 2, 60, CAST(N'2019-01-15 00:00:00.000' AS DateTime))
INSERT [dbo].[PWINRecord] ([ScoreId], [ProjectID], [Score], [Date]) VALUES (4, 2, 55, CAST(N'2019-01-15 00:00:00.000' AS DateTime))
INSERT [dbo].[PWINRecord] ([ScoreId], [ProjectID], [Score], [Date]) VALUES (5, 2, 72, CAST(N'2019-01-15 00:00:00.000' AS DateTime))
INSERT [dbo].[PWINRecord] ([ScoreId], [ProjectID], [Score], [Date]) VALUES (6, 4, 111, CAST(N'2019-01-16 00:00:00.000' AS DateTime))
INSERT [dbo].[PWINRecord] ([ScoreId], [ProjectID], [Score], [Date]) VALUES (7, 4, 90, CAST(N'2019-01-17 00:00:00.000' AS DateTime))
ALTER TABLE [dbo].[PWINRecord] WITH CHECK ADD CONSTRAINT [FK_PWINRecord_PWINProject] FOREIGN KEY([ProjectID])
REFERENCES [dbo].[PWINProject] ([ProjectID])
ALTER TABLE [dbo].[PWINRecord] CHECK CONSTRAINT [FK_PWINRecord_PWINProject]
-- To select your desired output run the below code
;WITH CTE
AS
(
SELECT p.ProjectID, p.Name, r.Score, r.[Date], ROW_NUMBER()OVER (PARTITION BY R.ProjectID ORDER BY r.[Date] DESC) RN
FROM PWINProject p
full join PWINRecord R on p.ProjectID =r.ProjectID
)
SELECT ProjectID, Name, Score, [Date]
FROM CTE
WHERE RN = 1

Top three records for each branch

I need to write query for top three record(count and sum) for each branch of company and I have branch costumers and contracts tables
First of all, create a table like this:
CREATE TABLE [dbo].[Branches](
[Id] [int] NOT NULL,
[CompanyId] [int] NULL,
[Something] [int] NULL,
CONSTRAINT [PK_Table_5] 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
Then, insert some data into it:
INSERT [dbo].[Branches] ([Id], [CompanyId], [Something]) VALUES (1, 1, 10)
GO
INSERT [dbo].[Branches] ([Id], [CompanyId], [Something]) VALUES (2, 1, 15)
GO
INSERT [dbo].[Branches] ([Id], [CompanyId], [Something]) VALUES (3, 1, 20)
GO
INSERT [dbo].[Branches] ([Id], [CompanyId], [Something]) VALUES (4, 1, 25)
GO
INSERT [dbo].[Branches] ([Id], [CompanyId], [Something]) VALUES (5, 1, 22)
GO
INSERT [dbo].[Branches] ([Id], [CompanyId], [Something]) VALUES (6, 2, 50)
GO
INSERT [dbo].[Branches] ([Id], [CompanyId], [Something]) VALUES (7, 2, 32)
GO
INSERT [dbo].[Branches] ([Id], [CompanyId], [Something]) VALUES (8, 2, 30)
GO
INSERT [dbo].[Branches] ([Id], [CompanyId], [Something]) VALUES (9, 2, 10)
GO
In the end, your query will be like this:
SELECT result.Companyid, SUM(result.Something) [Sum], (SELECT COUNT(*) FROM Branches WHERE CompanyId = result.companyid) AS [Count]
FROM (
SELECT companyid, Something, Rank()
OVER (PARTITION BY CompanyId
ORDER BY id DESC ) AS Rank
FROM Branches
) result WHERE Rank <= 3
GROUP BY CompanyId
Schema:
First data:
Result:

how to get table from first table when data is not there in second table

i have requirement where i need to show data of both tables when both the ID's are same.when id is present in first table and not there in second table i need to show data from first table
CREATE TABLE [dbo].[TEST](
[ID] [int] NULL,
[Name] [varchar](10) NULL,
[Status] [char](1) NULL,
[CreatedDate] [datetime] NULL
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[Test_History](
[ID] [int] NULL,
[Name] [varchar](10) NULL,
[Status] [char](1) NULL,
[CreatedDate] [datetime] NULL
) ON [PRIMARY]
GO
/****** Object: Table [dbo].[Test_History] Script Date: 06/19/2015 19:01:49 ******/
INSERT [dbo].[Test_History] ([ID], [Name], [Status], [CreatedDate]) VALUES (1, N'Mohan', N'A', CAST(0x0000A4BC01347E88 AS DateTime))
INSERT [dbo].[Test_History] ([ID], [Name], [Status], [CreatedDate]) VALUES (1, N'Mohan', N'I', CAST(0x0000A4BC0134A390 AS DateTime))
INSERT [dbo].[Test_History] ([ID], [Name], [Status], [CreatedDate]) VALUES (2, N'Rohan', N'A', CAST(0x0000A4BC01391FCC AS DateTime))
/****** Object: Table [dbo].[TEST] Script Date: 06/19/2015 19:01:49 ******/
INSERT [dbo].[TEST] ([ID], [Name], [Status], [CreatedDate]) VALUES (2, N'Rohan', N'I', CAST(0x0000A4BC0138D584 AS DateTime))
INSERT [dbo].[TEST] ([ID], [Name], [Status], [CreatedDate]) VALUES (1, N'Mohan', N'A', CAST(0x0000A4BC013072DC AS DateTime))
INSERT [dbo].[TEST] ([ID], [Name], [Status], [CreatedDate]) VALUES (3, N'Raj', N'A', CAST(0x0000A4BC0138DED7 AS DateTime))
INSERT [dbo].[TEST] ([ID], [Name], [Status], [CreatedDate]) VALUES (4, N'Krishna', N'A', CAST(0x0000A4BC0138EE31 AS DateTime))
so far i have tried my query to achieve the result
select T.ID,COALESCE(T.ID,TT.ID),T.Name,COALESCE(T.Name,TT.Name),T.status,COALESCE(T.status,TT.status)
from Test T LEFT JOIN (Select TOP 1 ID,MIN(Name)name,Status from Test_History
GROUP BY ID,status
)TT
ON T.ID = TT.ID
where T.ID = 3
Id = 1 and 2 present show i will get data from both tables
Id = 3 and 4 not present in the table
so using coalesce i will get the data
from first table and show in 2nd table column also
but is there any other way like both tables are same structure
i'm thinking of
Declare #tablename varchar(10)
IF EXISTS (SELECT 1 from TESt where id = #id)
IF COunt there in both tables
SET #tablename = Test
ELSE
SET #tablename = Test_history
select * from #tablename where id = #ID
can i get any solution like this
You can use EXCEPT.
Here is an example:
SELECT a,b
FROM (
VALUES (1, 2), (3, 4), (5, 6), (7, 8), (9, 10)
) AS MyTable(a, b)
EXCEPT
SELECT a,b
FROM (
VALUES (1, 2), (7, 8), (9, 10)
) AS MyTable(a, b);
This will return all rows of the upper statement, which are not in the second statement.
First: Thanks for the excellent setup for the data related to the question!
If your real question was if table variables can be used as described in your question, the answer is no; or more accurately that its not worth it.
Not recommended:
declare #TableName TABLE (
[ID] [int] NULL,
[Name] [varchar](10) NULL,
[Status] [char](1) NULL,
[CreatedDate] [datetime] NULL)
IF EXISTS (SELECT 1 from TESt where id = #id)
INSERT INTO #TableName SELECT * FROM dbo.TEST WHERE ID = #ID
ELSE INSERT INTO #TableName SELECT * FROM dbo.[Test_History] WHERE ID = #ID
select * from #tablename where id = #ID
Here's the solution I prefer:
DECLARE #ID INT = 3;
SELECT * FROM [dbo].[TEST] ss WHERE ss.id = #id
UNION ALL SELECT * FROM [dbo].[Test_History] th WHERE th.id = #id
and not exists ( SELECT * FROM [dbo].[TEST] ss WHERE ss.id = #id);
UNION ALL performs surprisingly well - don't forget the ALL keyword, and I am assuming that ID is a PK or AK.
If I'm understanding correctly and you want to display all records that match between the two tables and only records from first table when the id does not exist in the second in the same result set, then all you need is a simple left join:
SELECT *
FROM dbo.test t
LEFT OUTER JOIN Test_History th
ON t.id = th.id
WHERE t.id = #id