SQL Server join query not retrieving proper result - sql-server

Select the list of all dealers whose vehicles are never assigned to any customer i.e that vehicle is not present in PS3_VehicleBooking, and committed date is greater than today's date.
3 tables to be used are-
CREATE TABLE PS3_Dealer(
ID int IDENTITY(1,1) NOT NULL Primary KEY,
DealerID varchar(255) NULL,
DealerName varchar(255) NOT NULL,
ContactNo varchar(255) NOT NULL,
NoOfVehicles bigint NOT NULL,
CommittedDate date NOT NULL
);
create table PS3_Vehicle
(
ID int IDENTITY(1,1) NOT NULL Primary Key,
DealerID varchar(255) NOT NULL,
VehicleID varchar(255) UNIQUE NOT NULL,
VehicleName varchar(255) NOT NULL,
Capacity BIGINT NOT NULL
);
create table PS3_VehicleBooking
(
ID int IDENTITY(1,1) NOT NULL Primary Key,
BookingID varchar(255) NOT NULL UNIQUE,
VehicleID varchar(255) NOT NULL,
StartDate date NOT NULL,
EndDate date NOT NULL
);
PS3_Dealer values-
DealerID DealerName ContactNo NoOfVehicles CommittedDate
1 ng 9 5 2013-12-12
2 nikki 5 7 2013-12-25
3 nik 4 6 2013-10-11
4 hj 2 2 2014-11-10
5 pk 67 8 2013-10-10
PS3_Vehicle
DealerID VehicleID VehicleName Capacity
1 V1 ind 4
2 V2 innova 5
3 V3 innova 6
1 V4 ula 8
4 V5 hkk 2
5 V6 ghi 9
2 V7 bjhgi 4
PS3_VehicleBooking-
BookingID VehicleID StartDate EndDate
1 V1 2013-12-13 2013-12-17
2 V2 2013-10-11 2013-10-13
3 V3 2014-12-10 2014-12-13
4 V4 2012-10-10 2012-10-13
5 V2 2013-12-14 2013-12-18
expected outcome-
DealerID DealerName ContactNo NoOfVehicles CommittedDate
4 hj 2 2 2014-11-10
but i am getting-
DealerID DealerName ContactNo NoOfVehicles CommittedDate
4 hj 2 2 2014-11-10
2 Nikki 5 7 2013-12-25
i dont want dealer id 2 in result because V2 provided by dealer id 2 is present in PS3_Booking.
My query is-
SELECT h.DealerID,
h.DealerName,
h.ContactNo,
h.NoOfVehicles,
h.CommittedDate
FROM PS3_Dealer h
INNER JOIN(SELECT DealerID,
PS3_VehicleBooking.VehicleID
FROM PS3_Vehicle
LEFT JOIN PS3_VehicleBooking
ON PS3_Vehicle.VehicleID = PS3_VehicleBooking.VehicleID) w
ON h.DealerID = w.DealerID
WHERE w.VehicleID IS NULL
AND h.CommittedDate > GETDATE()
please correct where i am wrong?

The problem is that DealerID 2 has 2 Vehicles. One that has booking, and the other does not. When you run
SELECT DealerID,
PS3_VehicleBooking.VehicleID
FROM PS3_Vehicle
LEFT JOIN PS3_VehicleBooking
ON PS3_Vehicle.VehicleID = PS3_VehicleBooking.VehicleID
You will get one record that has DealerID 2 with NULL VehicleID and the other none NULL Vehicle ID.
You can try to the run the statement below to get the desired results (may not be the most optimal, but it is a fix to your statement):
SELECT h.DealerID,
h.DealerName,
h.ContactNo,
h.NoOfVehicles,
h.CommittedDate
FROM PS3_Dealer h
INNER JOIN(SELECT DealerID,
MAX(PS3_VehicleBooking.VehicleID) as VehicleID
FROM PS3_Vehicle
LEFT JOIN PS3_VehicleBooking
ON PS3_Vehicle.VehicleID = PS3_VehicleBooking.VehicleID
GROUP BY DealerID
) w
ON h.DealerID = w.DealerID
WHERE w.VehicleID IS NULL
AND h.CommittedDate > GETDATE()

Edited to actually answer the question
You were getting Nikki because there does exist a car for her (V7) that is not in the booking table.
This should exclude Nikki.
SELECT h.DealerID,
h.DealerName,
h.ContactNo,
h.NoOfVehicles,
h.CommittedDate
FROM PS3_Dealer d
WHERE NOT EXISTS
(SELECT *
FROM PS3_VehicleBookingb
JOIN PS3_Vehiclev ON b.vehicleID = v.VehicleID
JOIN PS3_Dealerd2 ON d2.dealerID = v.dealerID
WHERE d2.dealerID = d.dealerID)
AND CommittedDate > Current_date()
http://sqlfiddle.com/#!2/fb365/19

Related

T-SQL Get Values from Lookup table and use in view / stored procedure

I couldn't find that via search, so I guess I am not asking it the right way, so help is welcome.
We have a lookup table:
Id Name
------------------
1 "Test"
2 "New"
3 "InProgress"
Table2:
StatusId SomethingElse
1
2
Table 1
ID Other Other StatusId (Fkey to Table2) ...
Then we have a view that selects from several tables and one of the columns is a CASE Statement:
SELECT * FROM Table1 t1 -- has million records
CASE When t1.StatusId = 1 THEN (SELECT Name from LOOKUP table where ID = 1) END --'Test'
CASE When t1.StatusId = 2 THEN (SELECT Name from LOOKUP table where ID = 2) END --'New'
CASE When t3.Date is not null THEN (SELECT Name from LOOKUP table where ID = 3) END --'In Progress'
-- AND ALSO the case look at other tables another 5-6 tables and there are conditions from there
INNER JOIN Table2 t2 on ...
INNER JOIN Table3 t3 on ...
As you see these are really static values.
I want to load them once into variables, e.g.
#LookUp1 = SELECT [NAME] FROM LookUP WHERE Id = 1,
#LookUp2 = SELECT [NAME] FROM LookUP WHERE Id = 2
and replace the select in the CASE statement to this:
When StatusId = 1 THEN #LookUp
When StatusId = 2 THEN #LookUp2
The view loops through millions of records and it gets really slow to do the select from Lookup table for every row.
Why not simply use a join?
SELECT <columns list from main table>, Lt.Name
FROM <main table> As Mt -- Do not use such aliases in real code!
JOIN <SecondaryTable> As St -- this represents your Table3
ON <condition>
[LEFT] JOIN <Lookup table> As Lt
ON Mt.StatusId = Lt.Id
OR (Lt.Id = 3 AND St.Date is not null)
Of course, replace <columns list from main table> with the actual columns list, <main table> with the name of the main table and so on.
The join might be an inner or left join, depending on the nullability of the StatusId column in the main table and if it's nullable, on what you want to get in such cases (either a row with null name or no row at all).
I've put together a little demonstration to show you exactly what I mean.
Create and populate sample tables (Please save us this step in your future questions):
CREATE TABLE LookUp (Id int, Name varchar(10));
INSERT INTO LookUp (Id, Name) VALUES
(1, 'Test'), (2, 'New'), (3, 'InProgress');
CREATE TABLE Table1 (Id int not null, StatusId int null);
INSERT INTO Table1(Id, StatusId)
SELECT n, CASE WHEN n % 3 = 0 THEN NULL ELSE (n % 3) END
FROM
(
SELECT TOP 30 ROW_NUMBER() OVER(ORDER BY ##SPID) As n
FROM sys.objects
) tally
CREATE TABLE Table3
(
Id int not null,
Date date null
)
INSERT INTO Table3 (Id, Date)
SELECT Id, CASE WHEN StatusId IS NULL AND Id % 4 = 0 THEN GetDate() END
FROM Table1
The query:
SELECT Table1.Id,
Table1.StatusId,
Table3.Date,
LookUp.Name
FROM Table1
JOIN Table3
ON Table1.Id = Table3.Id
LEFT JOIN LookUp
ON Table1.StatusId = LookUp.Id
OR (LookUp.Id = 3 AND Table3.Date IS NOT NULL)
Results:
Id StatusId Date Name
1 1 NULL Test
2 2 NULL New
3 NULL NULL NULL
4 1 NULL Test
5 2 NULL New
6 NULL NULL NULL
7 1 NULL Test
8 2 NULL New
9 NULL NULL NULL
10 1 NULL Test
11 2 NULL New
12 NULL 27.06.2019 InProgress
13 1 NULL Test
14 2 NULL New
15 NULL NULL NULL
16 1 NULL Test
17 2 NULL New
18 NULL NULL NULL
19 1 NULL Test
20 2 NULL New
21 NULL NULL NULL
22 1 NULL Test
23 2 NULL New
24 NULL 27.06.2019 InProgress
25 1 NULL Test
26 2 NULL New
27 NULL NULL NULL
28 1 NULL Test
29 2 NULL New
30 NULL NULL NULL
You can also see a live demo on rextester.
Create a SQL function which return Name according to Id.
Create FUNCTION [dbo].[GetLookUpValue]
(
#Id int
)
RETURNS varchar(500)
AS BEGIN
return(Select Name from LOOKUP_table with(nolock) where Id=#Id)
END

parent id hierarchy identification MS SqlServer2012

I have this code
create table #temp
(
order_id int not null identity(1,1) primary key
,sid int
,created_date date
,parent_order_id int
)
insert into #temp
(
sid
,created_date
)values(1,'2017-01-01')
insert into #temp
(
sid
,created_date
,parent_order_id
)values(1,'2017-02-01',1),(1,'2017-03-01',2),(1,'2017-04-01',3)
insert into #temp
(
sid
,created_date
)values(1,'2017-06-01')
insert into #temp
(
sid
,created_date
,parent_order_id
)values(1,'2017-07-01',5),(1,'2017-08-01',6)
select * from #temp
Whenever parent_order_id is null which indicates it is a new order. After that customer can add items associated to that order. so we have parent_order_id filled for these associations. But I want to know what is the first order_id for each association child order.I am looking for an output like below.
`order_id sid created_date parent_order_id original_order_id
1 1 2017-01-01 NULL 1
2 1 2017-02-01 1 1
3 1 2017-03-01 2 1
4 1 2017-04-01 3 1
5 1 2017-06-01 NULL 4
6 1 2017-07-01 5 4
7 1 2017-08-01 6 4
`
any help is appreciated. Thanks in advance.
With the following piece of code you can get results you are expecting.
;WITH cte (order_id, original_order_id)
AS
(
SELECT order_id, order_id AS original_order_id
FROM #temp WHERE parent_order_id IS NULL
UNION ALL
SELECT o.order_id AS order_id, cte.original_order_id AS original_order_id
FROM #temp AS o
JOIN cte
ON o.parent_order_id = cte.order_id
)
SELECT #temp.order_id, #temp.sid, #temp.created_date, #temp.parent_order_id, cte.original_order_id
FROM #temp
JOIN cte ON cte.order_id=#temp.order_id
ORDER BY cte.order_id
Please be aware, that there are certain limits on recursion as this for CTE. Currently it is 100 which can be pushed up to 32767.

SQL where clause for self-referencing table

I have something like the following table:
CREATE TABLE [dbo].[Test]
(
[Id] [int] IDENTITY(1,1) NOT NULL,
[Title] [nvarchar](450) NULL,
[Description] [nvarchar](4000) NULL,
[Created] [datetime] NULL,
[OrgId] [int] NULL CONSTRAINT [FK_Test_OrgId] FOREIGN KEY REFERENCES Test(Id),
CONSTRAINT [PK_Test]
PRIMARY KEY CLUSTERED ([Id] ASC)
)
A new entry has OrgId = null. If an entry has been edited, a new row is created with OrgId set to its original parent. If an entry is edited multiple times, all children will have OrgId set to the Id of the original row. The created datetime provides the "order".
What I need to do is select only the newest versions.
Given the table below, I am looking to select only Id 3, 5 & 6
Id Title Description Created PreId
-----------------------------------------------------
1 Car Orginal car 2014-01-01 NULL
2 House Original house 2014-01-01 NULL
3 Bike Original bike 2014-01-01 NULL
4 Car Car updated 2014-06-01 1
5 Car Car updated again 2014-08-01 1
6 House house updated 2014-09-01 2
Any input appreciated.
Thanks.
Since all records are pointing at the original row (and not the previous one) :
SELECT ID, Title, DEscription, CREATED, PreID
FROM
(SELECT ID, Title, DEscription, CREATED, PreID,
ROW_NUMBER() over(partition by ISNULL(OrgID,id) order by id desc) rn
FROM test) A
where RN = 1
WITH Titles AS
(
SELECT DISTINCT Title
FROM dbo.Test
)
SELECT A.ID, Ti.Title, A.[Description], A.Created, A.OrgId
FROM Titles AS Ti
OUTER APPLY (SELECT TOP (1) ID, [Description], [Created], [OrgId]
FROM dbo.Test AS Te
WHERE Te.Title = Ti.Title
ORDER BY Created DESC
) AS A;

Updating first record of a group of records t-sql

I have a table like this
CREATE TABLE [dbo].[tblUserLink](
[IDUserLink] [int] IDENTITY(1,1) NOT NULL,
[IDUser] [int] NOT NULL,
[IDRegie] [varchar](50) NOT NULL,
[DefaultAgency] [bit] NULL
)
I'm looking for a query /script to update the field DefaultAgency. I'd like to set it to 1 for the first record that comes for each group of record with a same IDUser.
example :
IDUserLink IDUser IDRegie DefaultAgency (Goal)
1 1 1 null DefaultAgency to be set to 1
2 1 2 null
3 1 3 null
4 2 2 null DefaultAgency to be set to 1
5 2 1 null
6 3 1 null DefaultAgency to be set to 1
...etc
Can I achieve this using a simple sql query or should I script it ?
update tblUserLink
set DefaultAgency = 1
where IDUserLink in
(select min(IDUserLink) from tblUserLink group by IDUser)
If you're on SQL Server 2005+, you can use a common table expression:
with cte as (
select *,
row_number() over (partition by IDUser order by IDUserLink) as [rn]
from tblUserLink
)
update c
set DefaultAgency = 1
from cte as c
where c.rn = 1

CTE - recursively update quantity until total consumed

I've been researching CTEs trying to determine if it's possible to recursively update inventory quantity records with an order quantity until the order quantity is consumed.
Here are the tables and records:
CREATE TABLE [dbo].[myOrder](
[Account] [float] NOT NULL,
[Item] [float] NOT NULL,
[Quantity] [float] NOT NULL
) ON [PRIMARY]
insert into dbo.myOrder values (12345, 1, 50)
CREATE TABLE [dbo].[myInventory](
[ID] [int] IDENTITY(1,1) NOT NULL,
[Account] [float] NOT NULL,
[InvDate] [numeric](18, 0) NOT NULL,
[Item] [float] NOT NULL,
[Quantity] [float] NOT NULL,
[QuantitySold] [float] NOT NULL
) ON [PRIMARY]
insert into dbo.myInventory values (12345, 111287, 1, 45, 40)
insert into dbo.myInventory values (12345, 111290, 1, 40, 0)
insert into dbo.myInventory values (12345, 111290, 1, 12, 0)
insert into dbo.myInventory values (12345, 111291, 1, 25, 0)
The record in the myOrder table indicates that an order is to be created for account 12345 for item #1, quantity 50:
Account Item Quantity
------- ---- --------
12345 1 50
The inventory table shows that we have plenty of item #1 on hand for account 12345:
ID Account InvDate Item Quantity QuantitySold
-- ------- ------- ---- -------- ------------
1 12345 111287 1 45 40
2 12345 111290 1 40 0
3 12345 111290 1 12 0
4 12345 111291 1 25 0
The goal is to start plugging in the order quantity of 50 into the inventory records until all 50 are consumed. Inventory records are ordered by the value in the InvDate column. Record 1 has 5 remaining quantity (45 - 40 = 5), which would leave us with 45 more to consume for the order. Record 2 can consume 40. Record 3 can consume the last 5. When the query completes the inventory records would look like this:
ID Account InvDate Item Quantity QuantitySold
-- ------- ------- ---- -------- ------------
1 12345 111287 1 45 45
2 12345 111290 1 40 40
3 12345 111290 1 12 5
4 12345 111291 1 25 0
Note: The inventory table stores QuantitySold, not QuantityRemaining, so you have to do the math (Quantity - QuantitySold) to determine how much quantity remains per inventory record.
I've gotten almost nowhere with the CTE. I've found plenty of examples for doing selects where you have 2 parts to your CTE - an initialization part and the recursive part UNIONed together. I could write this with a cursor, but I think it's possible to do with a CTE and I'd like to learn how.
If anyone can confirm this is possible with a CTE or explain how to set up the CTE, I'd appreciate it. Thanks!
--#inserted table mimics inserted virtual table from AFTER INSERT triggers on [dbo].[myOrder] table
DECLARE #inserted TABLE
(
[Account] [float] NOT NULL,
[Item] [float] NOT NULL,
[Quantity] [float] NOT NULL
);
INSERT #inserted
VALUES (12345, 1, 50);
WITH CteRowNumber
AS
(
SELECT inv.ID
,inv.Account
,inv.Item
,inv.Quantity
,inv.QuantitySold
,i.Quantity QuantityOrdered
,ROW_NUMBER() OVER(PARTITION BY inv.Account,inv.Item ORDER BY inv.ID ASC) RowNumber
FROM myInventory inv
INNER JOIN #inserted i ON inv.Account = i.Account
AND inv.Item = i.Item
WHERE inv.Quantity > inv.QuantitySold
), CteRecursive
AS
(
SELECT a.ID
,a.Account
,a.Item
,a.RowNumber
,CASE
WHEN a.Quantity - a.QuantitySold < a.QuantityOrdered THEN a.Quantity - a.QuantitySold
ELSE a.QuantityOrdered
END QuantitySoldNew
,CASE
WHEN a.Quantity - a.QuantitySold < a.QuantityOrdered THEN a.Quantity - a.QuantitySold
ELSE a.QuantityOrdered
END RunningTotal
FROM CteRowNumber a
WHERE a.RowNumber = 1
UNION ALL
SELECT crt.ID
,crt.Account
,crt.Item
,crt.RowNumber
,CASE
WHEN prev.RunningTotal + (crt.Quantity - crt.QuantitySold) < crt.QuantityOrdered THEN crt.Quantity - crt.QuantitySold
ELSE crt.QuantityOrdered - prev.RunningTotal
END QuantitySoldNew
,CASE
WHEN prev.RunningTotal + (crt.Quantity - crt.QuantitySold) < crt.QuantityOrdered THEN prev.RunningTotal + (crt.Quantity - crt.QuantitySold)
ELSE crt.QuantityOrdered
END RunningTotal
FROM CteRecursive prev
INNER JOIN CteRowNumber crt ON prev.Account = crt.Account
AND prev.Item = crt.Item
AND prev.RowNumber + 1 = crt.RowNumber
WHERE prev.RunningTotal < crt.QuantityOrdered
)
SELECT cte.ID
,cte.Account
,cte.Item
,cte.QuantitySoldNew
FROM CteRecursive cte;
--or CteRecursive can be used to update QuantitySold column from [dbo].[myInventory] table
--UPDATE myInventory
--SET QuantitySold = inv.QuantitySold + cte.QuantitySoldNew
--FROM myInventory inv
--INNER JOIN CteRecursive cte ON inv.ID = cte.ID;

Resources