Self join to get the first classID for a report - sql

I have the following table :
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[myTable]
(
[importID] [INT] IDENTITY(1,1) NOT NULL,
[classID] [INT] NOT NULL,
[priorReportID] [INT] NOT NULL,
[currentReportID] [INT] NOT NULL,
[dateStamp] AS (GETDATE())
) ON [PRIMARY]
GO
SET IDENTITY_INSERT [dbo].[myTable] ON
GO
INSERT INTO [dbo].[myTable] ([importID], [classID], [priorReportID], [currentReportID])
VALUES (1, 2069, 3825, 3833), (2, 2069, 3826, 3834),
(3, 2069, 3827, 3835), (4, 2069, 3832, 3836),
(5, 2091, 3889, 3890), (6, 2095, 3894, 3895),
(7, 2098, 3895, 3898), (8, 2098, 3896, 3899),
(9, 2098, 3897, 3900), (10, 2097, 2190, 2193),
(11, 2096, 2188, 2190), (12, 2094, 2187, 2188),
(13, 2093, 2180, 2187)
GO
SET IDENTITY_INSERT [dbo].[myTable] OFF
GO
I am trying to get the first ClassID when a particular report was created.
select *
from mytable
select *
from mytable
where currentReportID = 3833
select *
from mytable
where currentReportID = 3825
select *
from mytable
where currentReportID = 2193
select *
from mytable
where currentReportID = 2190
select *
from mytable
where currentReportID = 2188
select *
from mytable
where currentReportID = 2187
select *
from mytable
where currentReportID = 2180
In the example above: reportID = 2193 was actually created in classID = 2093.
Similarly, reportID = 3833 was created in classID = 2069.
Basically, I need to traverse down the records until there is no more currentReportID = priorReportID condition is satisfied.
Thanks in advance.

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.

update transaction table data with new table id

I have 3 tables with following structure and data.
CREATE TABLE [dbo].[Department]
(
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) NOT NULL
)
CREATE TABLE [dbo].[Department_New]
(
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) NOT NULL
)
CREATE TABLE [dbo].[Employee]
(
[Id] [int] IDENTITY(1,1) NOT NULL,
[DepId] [int] NOT NULL,
[Name] [nvarchar](50) NOT NULL
)
SET IDENTITY_INSERT [dbo].[Department] ON
INSERT [dbo].[Department] ([Id], [Name])
VALUES (1, N'HR'), (2, N'IT'), (3, N'Account'), (4, N'Finance'), (5, N'Software')
SET IDENTITY_INSERT [dbo].[Department] OFF
SET IDENTITY_INSERT [dbo].[Department_New] ON
INSERT [dbo].[Department_New] ([Id], [Name])
VALUES (1, N'IT'), (2, N'HR'), (3, N'Software'), (4, N'Account'),
(5, N'iDontKnow'), (6, N'Finance')
SET IDENTITY_INSERT [dbo].[Department_New] OFF
SET IDENTITY_INSERT [dbo].[Employee] ON
INSERT [dbo].[Employee] ([Id], [DepId], [Name])
VALUES (1, 1, N'abc'), (2, 2, N'zxc'), (3, 1, N'xcv'),
(4, 3, N'cvb'), (5, 4, N'vbn'), (6, 1, N'bnm'),
(7, 3, N'asd'), (8, 3, N'sdf'), (9, 2, N'dfg'),
(10, 4, N'fgh'), (11, 5, N'ghj'), (12, 5, N'hjk'),
(13, 6, N'jkl'), (14, 6, N'qwe'), (15, 2, N'wre'),
(16, 3, N'ert'), (17, 6, N'rty'), (18, 1, N'tyu')
SET IDENTITY_INSERT [dbo].[Employee] OFF
As of now Employee table has old depId. I just want to update with new depId. I can not write for loop because employee table contains more than 1,000,000 rows of data.
What I have tried
CREATE PROCEDURE UpdateDepId
AS
BEGIN
DECLARE #totalRecords INT
DECLARE #I INT
SELECT #I = 1
SELECT #totalRecords = COUNT(DISTINCT DepId) FROM Employee
DECLARE #DPID INT
DECLARE #Id INT
--drop table #Temp
IF OBJECT_ID('tempdb.dbo.#Temp', 'U') IS NOT NULL
DROP TABLE #Temp;
CREATE TABLE #Temp
(
Id INT IDENTITY(1,1),
DepId INT
)
INSERT INTO #Temp
SELECT DISTINCT DepId
FROM Employee
ORDER BY DepId
WHILE (#I <= #totalRecords)
BEGIN
SET #DPID = (SELECT DepId FROM #Temp WHERE Id = #I)
EXEC UpdateDepByDepId #DPID
SELECT #I = #I + 1
END
DROP TABLE #Temp
END
CREATE PROCEDURE UpdateDepByDepId
#DepId INT
AS
BEGIN
DECLARE #DepIdNew INT
SET #DepIdNew = (SELECT Id FROM Department_New
WHERE Name IN (SELECT Name FROM Department WHERE id = #DepId))
SELECT #DepIdNew AS NewDepId, #DepId AS OldDepId
IF ISNULL(#DepIdNew, 0) <> 0
BEGIN
UPDATE Employee
SET DepId = #DepIdNew
WHERE DepId = #DepId
END
END
But it is not working for me. Please help
Use an update and from:
update e
set depid = dn.id
from employee e join
department d
on e.depid = d.id join
department_new dn
on dn.name = d.name;

Not using Where statement in a context

Goal:
If you have the input data that is -10 then you should not use the WHERE statement in function.
Problem:
I do not know how to solve it in this context. You have to use WHERE and not WHERE depending on what input data you retrieve
Info:
If you use -10 as a input data then you should retrieve all data based on [dbo].[testing] and it is okay to retrieve data that is null in [dbo].[testing2] in relation to LEFT JOIN.
*The code and its data is a sample from production phase.
Thank you!
CREATE TABLE [dbo].[testing](
[id] [int] NULL,
[value] [varchar](30) NULL,
[category] [int] NULL
) ON [PRIMARY]
CREATE TABLE [dbo].[testing2](
[id] [int] NULL,
[value] [varchar](30) NULL,
[category] [int] NULL,
[test_id] [int] NULL,
[id_type] [int] NOT NULL
) ON [PRIMARY]
CREATE FUNCTION dbo.testt (
#data int
)
RETURNS TABLE
AS
RETURN
(
SELECT
a.[id],
a.[value],
a.[category],
b.[id_type]
FROM [dbo].[testing] a left join [dbo].[testing2] b on a.id = b.[id]
where b.[id_type] = #data
)
INSERT INTO [test].[dbo].[testing] VALUES
(1, '', 2), (2, '', 3), (3, 'a', 2), (4, 'a', 2),
(5, 'b', 2), (6, 'b', 2), (7, 'c', 2), (8, 'c', 2),
(9, 'c', 2), (10, 'c', 2);
INSERT INTO [test].[dbo].[testing2] VALUES
(3, 'a' ,2 ,11 ,1), (4, 'a' ,2 ,11 ,1),
(5, 'a' ,2 ,11 ,0), (6, 'a' ,2 ,11 ,2);
select
s.[id],
s.[value],
s.[category],
s.[id_type]
from dbo.testt(1) s
Have your WHERE clause check if #data is either -10 or matches b.[id_type].
WHERE (#data = -10) OR (b.[id_type] = #data)
What about where b.[id_type] = #data OR #data = -10 in the testt function ?
So your function would be:
CREATE FUNCTION dbo.testt (
#data int
)
RETURNS TABLE
AS
RETURN
(
SELECT
a.[id],
a.[value],
a.[category],
b.[id_type]
FROM [dbo].[testing] a
LEFT JOIN [dbo].[testing2] b on a.id = b.[id]
WHERE b.[id_type] = #data OR #data = -10
)

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

SQL Server: How to update table based on subquery in where clause?

I have a table (with data) like this:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[lTab](
[log_id] [int] IDENTITY(1,1) NOT NULL,
[JobName] [nvarchar](40) NULL,
[startTime] [datetime] NULL,
[endTime] [datetime] NULL,
[BatchId] [int] NULL,
[status] [varchar](10) NULL,
[messag] [varchar](255) NULL
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
SET IDENTITY_INSERT [dbo].[lTab] ON
INSERT [dbo].[lTab] ([log_id], [JobName], [startTime], [endTime], [BatchId], [status], [messag]) VALUES (1, N'Job1', CAST(0x00009EB700FBF56F AS DateTime), NULL, 2, N'START', N'Test')
INSERT [dbo].[lTab] ([log_id], [JobName], [startTime], [endTime], [BatchId], [status], [messag]) VALUES (2, N'Job2', NULL, CAST(0x00009EB700FBF975 AS DateTime), 2, N'START', N'Test')
INSERT [dbo].[lTab] ([log_id], [JobName], [startTime], [endTime], [BatchId], [status], [messag]) VALUES (3, N'Job3', CAST(0x00009EB700FC287F AS DateTime), NULL, 2, N'START', N'Test')
INSERT [dbo].[lTab] ([log_id], [JobName], [startTime], [endTime], [BatchId], [status], [messag]) VALUES (4, N'Job3', NULL, CAST(0x00009EB700FC2CC6 AS DateTime), 2, N'END', N'Test')
SET IDENTITY_INSERT [dbo].[lTab] OFF
I'm trying to update endTime based on Jobname and max(log_id).
DECLARE #Jname VARCHAR(10)
SET #Jname = 'Job3'
UPDATE lTab
SET endTime = GETDATE()
WHERE log_id = (SELECT JobName, MAX(log_id) AS log_id FROM dbo.lTab WHERE jobname = #Jname GROUP BY JobName)
I get an error
sg 116, Level 16, State 1, Line 6
Only one expression can be specified in the select list when the subquery is not introduced with EXISTS.
How to get this work?
WITH q AS
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY JobName ORDER BY log_id DESC) AS rn
FROM lTab
WHERE JobName = #Jname
)
UPDATE q
SET endTime = GETDATE()
WHERE rn = 1
Take out the Jobname from the select list in the subquery.
You don't actually need it to get the result you need, SQL Server will still return the right log_id.
What you have won't work since you are returning 2 fields (Jobname,MAX(Log_id)) and trying to match Log_id to it.
This query:
(SELECT JobName, MAX(log_id) AS log_id FROM dbo.lTab WHERE jobname = #Jname GROUP BY JobName)
is returning multiple results, exclude JobName from your query.
Yet a better option would be
UPDATE tablename SET tablename.field1 = sourceTable.dataField
From tablename
join sourceTable On tablename.keyfield = sourceTable.keyField
Where sourceTable.jobname = #jobName