SQL SERVER - Left Join does not work. Because? [closed] - sql-server

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
This works perfectly in MS Access.
Why not in MS SQL Server?
Can you help me solve it?
Here's how my query is
select *
from tblPROestoque
where idproduto = 8183
order by identrada desc
select top(1) *
from tblPROestoque
where idproduto = 8183
order by identrada desc
select *
from tblPROproduto pr
left join (select top(1) idproduto, valcusto
from tblproestoque
order by identrada desc) tmp on tmp.idproduto = pr.idproduto
where pr.idproduto = 8183

I suspect the problem here is your understanding of how a subquery works. We have your query:
SELECT *
FROM tblPROproduto pr
LEFT JOIN (SELECT TOP (1)
idproduto,
valcusto
FROM tblproestoque
ORDER BY identrada DESC) tmp ON tmp.idproduto = pr.idproduto
WHERE pr.idproduto = 8183;
We can separate this into 2 different parts:
SELECT TOP (1)
idproduto,
valcusto
FROM tblproestoque
ORDER BY identrada DESC;
and then:
SELECT *
FROM tblPROproduto pr
LEFT JOIN tmp ON tmp.idproduto = pr.idproduto
WHERE pr.idproduto = 8183;
This might explain to you why what you have isn't working. I'm guessing you are assuming that the ON clause on tmp is derived BEFORE the SELECT of the subquery. This isn't the case. The subquery will be derived, and then the ON. Thus the value of tmp will be whatever is rerturned in the query above.
I suspect what you want is:
SELECT *
FROM tblPROproduto pr
OUTER APPLY (SELECT TOP (1)
ca.idproduto,
ca.valcusto
FROM tblproestoque ca
WHERE ca.idproduto = pr.idproduto
ORDER BY ca.identrada DESC) tmp
WHERE pr.idproduto = 8183;
Edit: Added some sample data and explanations for the OP, to help their udnerstanding:
USE Sandbox;
GO
CREATE TABLE Product (ID int IDENTITY(1,1),
Sku varchar(10),
ProductName varchar(25));
CREATE TABLE ProductOrder (ID int IDENTITY(1,1),
ProductID int,
OrderDate date,
NumberOrdered int);
INSERT INTO dbo.Product (Sku,
ProductName)
VALUES ('65432462','Lawn Mower'),
('98742347','Helicopter'),
('89465735','BBQ');
INSERT INTO dbo.ProductOrder (ProductID,
OrderDate,
NumberOrdered)
VALUES (1,'20180101',7),
(1,'20180708',19),
(2,'20180501',12),
(3,'20180804',27);
GO
SELECT *
FROM dbo.Product;
SELECT *
FROM dbo.ProductOrder;
GO
--Use the example the OP has in their post:
SELECT *
FROM dbo.Product P
LEFT JOIN (SELECT TOP 1 *
FROM dbo.ProductOrder
ORDER BY OrderDate DESC) PO ON PO.ProductID = P.ID
WHERE P.ID = 2;
--This returns NULLs for all the latter columns.
--Why?
--Inspect the subquery:
SELECT TOP 1 *
FROM dbo.ProductOrder
ORDER BY OrderDate DESC;
--Product ID 3? 3 != 2 so the ON clause fails:
--Demonstrate
SELECT *
FROM dbo.Product P
CROSS JOIN (SELECT TOP 1 * --CROSS JOIN joins all rows (creates a cartesian product)
FROM dbo.ProductOrder
ORDER BY OrderDate DESC) PO
WHERE P.ID = 2;
--The solution, use OUTER APPLY:
SELECT *
FROM dbo.Product P
OUTER APPLY (SELECT TOP 1 *
FROM dbo.ProductOrder oa
WHERE oa.ProductID = P.ID --WHERE clause, this is like your ON
ORDER BY oa.OrderDate DESC) PO
WHERE P.ID = 2;
GO
DROP TABLE dbo.ProductOrder;
DROP TABLE dbo.Product;

#RafaelBueno, you basically need an outer apply. This is different in MSAcess.
See modified below:
Let me know if it works.
select *
from tblPROproduto pr
outer apply (select top(1) idproduto, valcusto
from tblproestoque tmp
where tmp.idproduto = pr.idproduto
order by identrada desc) tmp
where pr.idproduto = 8183

Related

Print results side by side SQL Server

I have following result set,
Now with above results i want to print the records via select query as below attached image
Please note, I will have only two types of columns in output Present Employee & Absent Employees.
I tried using pivot tables, temporary table but cant achieve what I want.
One method would be to ROW_NUMBER each the the "statuses" and then use a FULL OUTER JOIN to get the 2 datasets into the appropriate columns. I use a FULL OUTER JOIN as I assume you could have a different amount of employees who were present/absent.
CREATE TABLE dbo.YourTable (Name varchar(10), --Using a name that doesn't require delimit identification
Status varchar(7), --Using a name that doesn't require delimit identification
Days int);
GO
INSERT INTO dbo.YourTable(Name, Status, Days)
VALUES('Mal','Present',30),
('Jess','Present',20),
('Rick','Absent',30),
('Jerry','Absent',10);
GO
WITH RNs AS(
SELECT Name,
Status,
Days,
ROW_NUMBER() OVER (PARTITION BY Status ORDER BY Days DESC) AS RN
FROM dbo.YourTable)
SELECT P.Name AS PresentName,
P.Days AS PresentDays,
A.Name AS AbsentName,
A.Days AS AbsentDays
FROM (SELECT R.Name,
R.Days,
R.Status,
R.RN
FROM RNs R
WHERE R.Status = 'Present') P
FULL OUTER JOIN (SELECT R.Name,
R.Days,
R.Status,
R.RN
FROM RNs R
WHERE R.Status = 'Absent') A ON P.RN = A.RN;
GO
DROP TABLE dbo.YourTable;
db<>fiddle
2 CTE's is actually far neater:
WITH Absents AS(
SELECT Name,
Status,
Days,
ROW_NUMBER() OVER (ORDER BY Days DESC) AS RN
FROM dbo.YourTable
WHERE Status = 'Absent'),
Presents AS(
SELECT Name,
Status,
Days,
ROW_NUMBER() OVER (ORDER BY Days DESC) AS RN
FROM dbo.YourTable
WHERE Status = 'Present')
SELECT P.Name AS PresentName,
P.Days AS PresentDays,
A.Name AS AbsentName,
A.Days AS AbsentDays
FROM Absents A
FULL OUTER JOIN Presents P ON A.RN = P.RN;

Return only rows from the joined table with the latest date

By running the following query I realized that I have duplicates on the column QueryExecutionId.
SELECT DISTINCT qe.QueryExecutionid AS QueryExecutionId,
wfi.workflowdefinitionid AS FlowId,
qe.publishing_date AS [Date],
c.typename AS [Type],
c.name As Name
INTO #Send
FROM
[QueryExecutions] qe
JOIN [Campaign] c ON qe.target_campaign_id = c.campaignid
LEFT JOIN [WorkflowInstanceCampaignActivities] wfica ON wfica.queryexecutionresultid = qe.executionresultid
LEFT JOIN [WorkflowInstances] wfi ON wfica.workflowinstanceid = wfi.workflowinstanceid
WHERE qe.[customer_idhash] IS NOT NULL;
E.g. When I test with one of these QueryExecutionIds, I can two results
select * from ##Send
where QueryExecutionId = 169237
We realized the reason is that these two rows have a different FlowId (second returned value in the first query). After discussing this issue, we decided to take the record with a FlowId that has the latest date. This date is a column called lastexecutiontime that sits in the third joined table [WorkflowInstances] which is also the table where FlowId comes from.
How do I only get unique values of QueryExecutionId with the latest value of WorkflowInstances.lastexecution time and remove the duplicates?
You can use a derived table with first_value partitioned by workflowinstanceid ordered by lastexecutiontime desc:
SELECT DISTINCT qe.QueryExecutionid AS QueryExecutionId,
wfi.FlowId,
qe.publishing_date AS [Date],
c.typename AS [Type],
c.name As Name
INTO #Send
FROM
[QueryExecutions] qe
JOIN [Campaign] c ON qe.target_campaign_id = c.campaignid
LEFT JOIN [WorkflowInstanceCampaignActivities] wfica ON wfica.queryexecutionresultid = qe.executionresultid
LEFT JOIN
(
SELECT DISTINCT workflowinstanceid, FIRST_VALUE(workflowdefinitionid) OVER(PARTITION BY workflowinstanceid ORDER BY lastexecutiontime DESC) As FlowId
FROM [WorkflowInstances]
) wfi ON wfica.workflowinstanceid = wfi.workflowinstanceid
WHERE qe.[customer_idhash] IS NOT NULL;
Please note that your distinct query is pertaining to the selected variables,
eg. Data 1 (QueryExecutionId = 169237 and typename = test 1)
    Data 2 (QueryExecutionId = 169237 and typename = test 2)
The above 2 data are considered as distinct
Try partition by and selection the [seq] = 1 (the below code are partition by their date)
SELECT *
into #Send
FROM
(
SELECT *,ROW_NUMBER() OVER (PARTITION BY [QueryExecutionid] ORDER BY [Date] DESC) [Seq]
FROM
(
SELECT qe.QueryExecutionid AS QueryExecutionId,
wfi.FlowId,
qe.publishing_date AS [Date], --should not have any null values
qe.[customer_idhash]
c.typename AS [Type],
c.name As Name
FROM [QueryExecutions] qe
JOIN [Campaign] c
ON qe.target_campaign_id = c.campaignid
LEFT JOIN [WorkflowInstanceCampaignActivities] wfica
ON wfica.queryexecutionresultid = qe.executionresultid
LEFT JOIN
(
SELECT DISTINCT workflowinstanceid, FIRST_VALUE(workflowdefinitionid) OVER(PARTITION BY workflowinstanceid ORDER BY lastexecutiontime DESC) As FlowId
FROM [WorkflowInstances]
) wfi ON wfica.workflowinstanceid = wfi.workflowinstanceid
) a
WHERE [customer_idhash] IS NOT NULL
) b
WHERE [Seq] = 1
ORDER BY [QueryExecutionid]

T SQL Update rows using max function

With this query what I need SQL to do is update a column value based on another column so that it grabs the most recent MAX ID.
select
max(T1id), t1.v_code
from
[Table1] T1
join
[Table2] t2 on t1.T1Code = t2.T2Code
where
t2.active = 0
and t1.t1activesw = 0
and t1.mapping not like '%selected%'
group by
t1.v_code
I'd like to join the select to the table on the version code id = max(t1.v_code) and then use the code as a sub select just not sure how to finish it.
With this, you get all data from the row for with the highest T1.id for each T1.v_code:
;WITH CTE as
(
SELECT
T1.*,
row_number() over (partition by T1.v_code order by T1.id desc) rn
FROM
[Table1] T1
JOIN
[Table2] t2 on t1.T1Code = t2.T2Code
WHERE
t2.active = 0
and t1.t1activesw = 0
and t1.mapping not like '%selected%'
)
SELECT *
FROM CTE
WHERE rn = 1
Edit: In order to update, replace(as requested in commment)
SELECT *
FROM CTE
WHERE rn = 1
with
UPDATE CTE
SET columnname = 'newvalue'
WHERE rn = 1
this

SQL Server Full Text Search - Weighting Certain Columns Over Others

If I have the following full text search query:
SELECT *
FROM dbo.Product
INNER JOIN CONTAINSTABLE(Product, (Name, Description, ProductType), 'model') ct
ON ct.[Key] = Product.ProductID
Is it possible to weigh the columns that are being searched?
For example, I care more about the word model appearing in the Name column than I do the
Description or ProductType columns.
Of course if the word is in all 3 columns then I would expect it to rank higher than if it was just in the name column. Is there any way to have a row rank higher if it just appears in Name vs just in Description/ProductType?
You can do something like the following query. Here, WeightedRank is computed by multiplying the rank of the individual matches. NOTE: unfortunately I don't have Northwind installed so I couldn't test this, so look at it more like pseudocode and let me know if it doesn't work.
declare #searchTerm varchar(50) = 'model';
SELECT 100 * coalesce(ct1.RANK, 0) +
10 * coalesce(ct2.RANK, 0) +
1 * coalesce(ct3.RANK, 0) as WeightedRank,
*
FROM dbo.Product
LEFT JOIN
CONTAINSTABLE(Product, Name, #searchTerm) ct1 ON ct1.[Key] = Product.ProductID
LEFT JOIN
CONTAINSTABLE(Product, Description, #searchTerm) ct2 ON ct2.[Key] = Product.ProductID
LEFT JOIN
CONTAINSTABLE(Product, ProductType, #searchTerm) ct3 ON ct3.[Key] = Product.ProductID
order by WeightedRank desc
Listing 3-25. Sample Column Rank-Multiplier Search of Pro Full-Text Search in SQL Server 2008
SELECT *
FROM (
SELECT Commentary_ID
,SUM([Rank]) AS Rank
FROM (
SELECT bc.Commentary_ID
,c.[RANK] * 10 AS [Rank]
FROM FREETEXTTABLE(dbo.Contributor_Birth_Place, *, N'England') c
INNER JOIN dbo.Contributor_Book cb ON c.[KEY] = cb.Contributor_ID
INNER JOIN dbo.Book_Commentary bc ON cb.Book_ID = bc.Book_ID
UNION ALL
SELECT c.[KEY]
,c.[RANK] * 5
FROM FREETEXTTABLE(dbo.Commentary, Commentary, N'England') c
UNION ALL
SELECT ac.[KEY]
,ac.[RANK]
FROM FREETEXTTABLE(dbo.Commentary, Article_Content, N'England') ac
) s
GROUP BY Commentary_ID
) s1
INNER JOIN dbo.Commentary c1 ON c1.Commentary_ID = s1.Commentary_ID
ORDER BY [Rank] DESC;
Similar to Henry's solution but simplified, tested and using the details the question provided.
NB: I ran performance tests on both the union and left join styles and found the below to require far less logical reads on the union style below with my datasets YMMV.
declare #searchTerm varchar(50) = 'model';
declare #nameWeight int = 100;
declare #descriptionWeight int = 10;
declare #productTypeWeight int = 1;
SELECT ranksGroupedByProductID.*, outerProduct.*
FROM (SELECT [key],
Sum([rank]) AS WeightedRank
FROM (
-- Each column that needs to be weighted separately
-- should be added here and unioned with the other queries
SELECT [key],
[rank] * #nameWeight as [rank]
FROM Containstable(dbo.Product, [Name], #searchTerm)
UNION ALL
SELECT [key],
[rank] * #descriptionWeight as [rank]
FROM Containstable(dbo.Product, [Description], #searchTerm)
UNION ALL
SELECT [key],
[rank] * #productTypeWeight as [rank]
FROM Containstable(dbo.Product, [ProductType], #searchTerm)
) innerSearch
-- Grouping by key allows us to sum each ProductID's ranks for all the columns
GROUP BY [key]) ranksGroupedByProductID
-- This join is just to get the full Product table columns
-- and is optional if you only need the ordered ProductIDs
INNER JOIN dbo.Product outerProduct
ON outerProduct.ProductID = ranksGroupedByProductID.[key]
ORDER BY WeightedRank DESC;

Return 5 Newest Articles for Each Category in MS SQL Server

Let's say I have a table Articles on a SQL Server 2008 database with the columns ID INT, Title VARCHAR(100), CatID INT, Posted DATETIME.
To get the 5 newest articles for a particular category, I can do this.
SELECT TOP (5) * FROM Articles WHERE CatID = #CatID ORDER BY Posted DESC
But what if I want the 5 newest articles for each category? I know I can repeat the query above for each category, but is there any way to do a single query that will return the 5 newest articles for each category?
EDIT:
Here's is the actual query I'm using to return the 5 newest articles with the section #SectionID. According to the actual terminology I'm using, it's the "section" I'm grouping by, not "category".
SELECT TOP (5) *
FROM Article
INNER JOIN Subcategory on Article.ArtSubcategoryID = Subcategory.SubID
INNER JOIN Category on Subcategory.SubCatID = Category.CatID
INNER JOIN section ON Category.CatSectionID = Section.SecID
WHERE (Section.SecID = #SectionID)
ORDER BY Article.ArtUpdated DESC
EDIT 2:
And here's the query I came up with based on comments here. Seems to work okay.
SELECT *
FROM (
SELECT Article.*,
ROW_NUMBER() OVER (PARTITION BY SecID ORDER BY ArtUpdated DESC) AS rn
FROM Article
INNER JOIN Subcategory on Article.ArtSubcategoryID = Subcategory.SubID
INNER JOIN Category on Subcategory.SubCatID = Category.CatID
INNER JOIN section ON Category.CatSectionID = Section.SecID
) q
WHERE rn <= 5
SELECT *
FROM (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY CatId ORDER BY Posted DESC) AS rn
FROM Articles
) q
WHERE rn <= 5
Try this
;WITH CTE AS (SELECT ROW_NUMBER() OVER(PARTITION BY CatID ORDER BY Posted DESC)
AS Rownum,*
FROM Articles )
SELECT * FROM CTE WHERE Rownum <= 5

Resources