I want to select rows from non-indexed view by their IDs.
Here is view definition:
CREATE VIEW [dbo].[_V_V3]
AS
SELECT (CONVERT(varchar,T1.PK_ID)+','+CONVERT(varchar,T2.PK_ID)) as ID,
T1.[Id] as [T1_Id],
T1.[V1] as [T1_V1],
T2.[Id] as [T2_Id],
T2.[V1] as [T2_V1]
FROM [T1] INNER JOIN [T2] ON (T2.V1=T1.V1)
where T2.V1, T1.V1 - nvarchar.
My select query:
SELECT * FROM [dbo].[_V_V3] WHERE ID IN ('1,1', '2,3', ....)
It works very slowly. With 1000 IDs this query could perform several minutes.
Is there any way to optimize this select?
Can you just do this:
View
CREATE VIEW [dbo].[_V_V3]
AS
T1.PK_ID as PK_ID1,
T2.PK_ID as PK_ID2,
T1.[Id] as [T1_Id],
T1.[V1] as [T1_V1],
T2.[Id] as [T2_Id],
T2.[V1] as [T2_V1]
FROM [T1] INNER JOIN [T2] ON (T2.V1=T1.V1)
Query
SELECT * FROM [dbo].[_V_V3]
WHERE
(
PK_ID1=1 AND PK_ID2=1
)
OR
(
PK_ID1=2 AND PK_ID2=3
)
I would think that an integer compare is fast then varchar compare
Related
I have a query in SQL Server with 6 JOINs and 1 LEFT JOIN to tables and views. It returns 16k records in about 1 second if the select clause is "SELECT *"
As soon as I specify even one column to display (SELECT ItemID, for example) the query slows down to about 70 seconds.
Query #1 (2s) - SELECT *:
SELECT *
FROM (SELECT LinkedToSet, LinkedToCopy, ',' + STRING_AGG(LocationID,',') + ',' Locs, count(1) OVER (PARTITION BY LinkedToSet) Copies
FROM Inventory.Locations WHERE LinkedToSet is not null AND (State & 4096)>0 GROUP BY LinkedToSet, LinkedToCopy) l
JOIN Bricklink_Set_Query bsq on l.LinkedToSet=bsq.Number
JOIN Bricklink.Set_Parts_Query bsp on l.LinkedToSet=bsp.SetNum AND bsp.Extra=0
JOIN Bricklink.Item_List i on bsp.ItemType=i.ItemType AND bsp.ItemID=i.Number
JOIN Bricklink.Category_List cat on i.Category_ID=cat.CatID
JOIN Bricklink.Color_List col on bsp.ColorID=col.ColorID
LEFT JOIN (SELECT LocationID, ItemType, ItemNum, ColorID, sum(QtyFound) as InvPcs
FROM Inventory.Item_History
GROUP BY LocationID, ItemType, ItemNum, ColorID) as h ON l.Locs like concat('%,',h.locationID,',%') AND h.ItemType=bsp.ItemType AND h.ItemNum=bsp.ItemID AND h.ColorID=bsp.ColorID
Actual Execution Plan: https://www.brentozar.com/pastetheplan/?id=SJD7Qemf_
Query #2 (81s) - SELECT a single column
SELECT bsp.ItemID
FROM (SELECT LinkedToSet, LinkedToCopy, ',' + STRING_AGG(LocationID,',') + ',' Locs, count(1) OVER (PARTITION BY LinkedToSet) Copies
FROM Inventory.Locations WHERE LinkedToSet is not null AND (State & 4096)>0 GROUP BY LinkedToSet, LinkedToCopy) l
JOIN Bricklink_Set_Query bsq on l.LinkedToSet=bsq.Number
JOIN Bricklink.Set_Parts_Query bsp on l.LinkedToSet=bsp.SetNum AND bsp.Extra=0
JOIN Bricklink.Item_List i on bsp.ItemType=i.ItemType AND bsp.ItemID=i.Number
JOIN Bricklink.Category_List cat on i.Category_ID=cat.CatID
JOIN Bricklink.Color_List col on bsp.ColorID=col.ColorID
LEFT JOIN (SELECT LocationID, ItemType, ItemNum, ColorID, sum(QtyFound) as InvPcs
FROM Inventory.Item_History
GROUP BY LocationID, ItemType, ItemNum, ColorID) as h ON l.Locs like concat('%,',h.locationID,',%') AND h.ItemType=bsp.ItemType AND h.ItemNum=bsp.ItemID AND h.ColorID=bsp.ColorID
Actual execution plan: https://www.brentozar.com/pastetheplan/?id=BJTr4x7Gu
The execution plans look totally different from each other and I'm not sure why. I've also tried wrapping the SELECT * and querying that, but some of these tables/views have the exact same field names, especially on the joins, so SQL Server throws an error:
This column 'foo' was specified multiple times.
How do I achieve the performance of SELECT * but limit which columns I display?
P.S. 2 Notes - 1) My desired select statement is obviously more complex than this and 2) Even using the full select statement, if I add a WHERE clause and restrict the query there, it runs in <1 second. If that plan would be useful I can post it as well.
First Left JOIN with 2 tables (project_reviews & project_review_remarks)
SELECT pr.review_id,client_name,improvement_areas, strengths
FROM project_reviews pr
LEFT JOIN project_review_remarks pra ON pr.review_id = pra.review_id
WHERE review_status IN ('COMPLETED')
ORDER BY review_date DESC;
Second Left JOIN with 2 tables (project_reviews & project_review_remarks)
SELECT pr.review_id, COUNT(pra.action_item_id) AS actions
FROM project_reviews pr
LEFT JOIN project_review_action_item pra ON pr.review_id = pra.review_id
GROUP BY pr.review_id;
In both the queries, project_reviews is a common table,
IN first Query is ONE-ONE relationship
IN second Query is ONE-MANY relationship (WHERE I COUNTED it many rows using group by)
I want to merge both queries into one query, because project_reviews is a common table with review ID and show the count of action_item into one table.
Just add these code to first query.
In select add COUNT(pra.action_item_id) AS actions.
Add LEFT JOIN project_review_action_item pra ON pr.review_id = pra.review_id. Make sure that your both LEFT JOIN tables has different alias as I have updated project_review_remarksprr.
Add GROUP BY with all columns from SELECT like GROUP BY pr.review_id, client_name, improvement_areas, strengths.
To GROUP BY with TEXT columns you can cast it to VARCHAR(MAX) or NVARCHAR(MAX) whichever you think appropriate.
Check complete query below.
SELECT pr.review_id,
client_name,
CAST(improvement_areas AS VARCHAR(MAX)) AS improvement_areas,
CAST(strengths AS VARCHAR(MAX)) AS strengths,
COUNT(pra.action_item_id) AS actions
FROM project_reviews pr
LEFT JOIN project_review_remarks prr
ON pr.review_id = prr.review_id
LEFT JOIN project_review_action_item pra
ON pr.review_id = pra.review_id
WHERE pr.review_status IN ('COMPLETED')
GROUP BY pr.review_id,
client_name,
CAST(improvement_areas AS VARCHAR(MAX)),
CAST(strengths AS VARCHAR(MAX))
ORDER BY review_date DESC;
I have this query in MySQL:
SELECT pr.*, pr7.value AS `room_price_high`
FROM `jos_hp_properties` pr
LEFT OUTER JOIN `jos_hp_properties2` pr7 ON pr7.property=pr.id
WHERE pr7.field=23
The jos_hp_properties table has 27 rows but the query only returns one. Based on this question I think it may be because of the WHERE clause. The jos_hp_properties2 table has fields id, property, field, value, where field is a foreign key to a third table (which I don't need to get data from).
Is there a way to select all the rows from the first table, including the value from table #2 where the field is 23 (or NULL if there is no field 23)?
Sure. Move the WHERE condition to the JOIN:
SELECT pr.*, pr7.value AS `room_price_high`
FROM `jos_hp_properties` pr
LEFT JOIN `jos_hp_properties2` pr7
ON pr7.property=pr.id
AND
pr7.field=23
You must place the pr7 criteria in the join, not in the where clause. The where clause works on the entire result set AFTER the join has been performed.
SELECT pr.*, pr7.value AS `room_price_high`
FROM `jos_hp_properties` pr
LEFT OUTER JOIN `jos_hp_properties2` pr7 ON pr7.property=pr.id and pr7.field=23
Try this:
SELECT pr.*, pr7.value AS `room_price_high`
FROM `jos_hp_properties` pr
LEFT OUTER JOIN `jos_hp_properties2` pr7 ON pr7.property=pr.id
WHERE (pr7.field=23 OR pr7.field is null)
You can also use a CTE (Common Table Expression) to do the select, then use the CTE to do the left join..
wrc (parentid, childid) as (
select parentid, childid
from placechild
where relationshipid in (select id from placerelationship where relationship = 'Winter Region Capital')
),
stw (cnid, coid, capid, st_or_te, sid, scid,wcid) as (
select s.cnid, s.coid, s.capid, s.st_or_te, s.sid, s.scid, w.childid
from stcap s
left join wrc w
on s.sid = w.parentid
)
select * from stw
below is T-SQL generated by application
DECLARE #xml_0 XML
SET #xml_0 = N'<Val>8e4cd3e3-de98-4f55-9c55-57881157a0f0</Val>
<Val>2f483275-7333-4786-aca8-454e5bf4823f</Val>
<Val>ce1ce763-1f68-48ec-bedf-f4641e40d8f8</Val>
<Val>6b471d5e-fd5c-4db8-aa31-abb910651e18</Val>
<Val>89064e42-0592-4845-b21e-38f788ab0d2e</Val>
<Val>d54793f0-cbfb-428e-ba08-db70cab1af07</Val>
<Val>8027e6bd-09e5-4a5b-aae7-54aff4a0e6c0</Val>
<Val>53f1a5e3-b2a8-49c3-935b-a5ac7fe0c1d8</Val>
<Val>faceabad-1d0c-4f3f-8d94-674bbf1c3428</Val>
<Val>f8e0a43d-cff7-45aa-b73f-6858b1d17cd1</Val>
<Val>94e9bc76-5bb3-4cf9-9b59-fc3163c904d7</Val>
<Val>e4be8c69-5166-40cc-b49a-18adec78e356</Val>
<Val>5c564b82-64e1-46c5-a41d-bc30104f14a5</Val>
<Val>dc246c2c-7edd-407a-b378-747789bd5a75</Val>
<Val>411ac1e9-3d4f-447c-808a-b82d388816dd</Val>'
SELECT COUNT(*) FROM
(
SELECT [t0].[ID]
FROM [dbo].[HM_Rows] AS [t0], [dbo].[HM_Cells] AS [t1]
WHERE
(
[t1].[Value] IN
(
SELECT node.value('.', 'NVARCHAR(200)') FROM #xml_0.nodes('/Val') xml_0(node)
)
)
) AS [r2487772634]
here is execution plan of T-SQL above
so it scans index
it scans correct indexes
missing_index_FOR_Value_INC_RowID - on HM_Cells table
and
PK_HM_Rows - on HM_Rows table
any idea?
P.S tables are large
Row counts
HM_Rows - 17'736'181
HM_Cells - 1'048'693'775
AND YES i have rebuilded indexes and updated statistics
HM_Cells.Value is NVarChar(200)
also without XML and HM_Rows table it working fine
e.g
SELECT ID FROM HM_Cells WHERE Value IN (.........)
works excellent
Thanks a lot :)
Try using a JOIN instead of an IN, because this way you can force a loop strategy, which will probably use a seek instead of a scan:
SELECT COUNT(*) FROM
(
SELECT [t0].[ID]
FROM (
SELECT DISTINCT node.value('.', 'NVARCHAR(200)') AS Val
FROM #xml_0.nodes('/Val') xml_0(node)
) q1 INNER LOOP JOIN [dbo].[HM_Cells] AS [t1] ON q1.Val=t1.Value
CROSS JOIN [dbo].[HM_Rows] AS [t0]
) AS [r2487772634]
I am currently having trouble with my query after joining two tables I am getting duplicated records. What i want to do is get the details from table 1 and the payment record from table 2 with same date.. here is my query.
SELECT dbo.tblautoipekonek.ipno,
dbo.tblautoipekonek.awbno,
dbo.tblautoipekonek.ipamount,
dbo.tblautoipekonek.orno,
dbo.tbladdfunds.username,
dbo.tbladdfunds.newctfbal,
dbo.tbladdfunds.addedctfamt,
dbo.tbladdfunds.oldctfbal,
dbo.tbladdfunds.newipbal,
dbo.tbladdfunds.addedipamt,
dbo.tbladdfunds.oldipbal,
dbo.tbladdfunds.date,
dbo.tblautoipekonek.date AS Expr1
FROM dbo.tblautoipekonek
LEFT OUTER JOIN dbo.tbladdfunds
ON dbo.tblautoipekonek.date = dbo.tbladdfunds.date
WHERE
( dbo.tblautoipekonek.date = '06/06/2014' )
Hi you can use "Distinct" keyword at after "select" keyword. it will get the single record.
Good to replace dbo.tablename with alias name give to tablename as
select
distinct
auto.IPNo, ... ---here you can add other column of this table.
fund.AddedCTFAmt --same thing done here.
FROM tblAutoIPEkonek auto
LEFT OUTER JOIN dbo.tblAddfunds fund
ON auto.Date = fund.Date --try to add more join condition so filteration done here of data which is fastest/
WHERE (auto.Date = '06/06/2014')
SELECT DISTINCT dbo.tblautoipekonek.ipno,
dbo.tblautoipekonek.awbno,
dbo.tblautoipekonek.ipamount,
dbo.tblautoipekonek.orno,
dbo.tbladdfunds.username,
dbo.tbladdfunds.newctfbal,
dbo.tbladdfunds.addedctfamt,
dbo.tbladdfunds.oldctfbal,
dbo.tbladdfunds.newipbal,
dbo.tbladdfunds.addedipamt,
dbo.tbladdfunds.oldipbal,
dbo.tbladdfunds.date,
dbo.tblautoipekonek.date AS Expr1
FROM dbo.tblautoipekonek
LEFT OUTER JOIN dbo.tbladdfunds
ON dbo.tblautoipekonek.date = dbo.tbladdfunds.date
WHERE
( dbo.tblautoipekonek.date = '06/06/2014' )
GROUP BY dbo.tblautoipekonek.ipno,
dbo.tblautoipekonek.awbno,
dbo.tblautoipekonek.ipamount,
dbo.tblautoipekonek.orno,
dbo.tbladdfunds.username,
dbo.tbladdfunds.newctfbal,
dbo.tbladdfunds.addedctfamt,
dbo.tbladdfunds.oldctfbal,
dbo.tbladdfunds.newipbal,
dbo.tbladdfunds.addedipamt,
dbo.tbladdfunds.oldipbal,
dbo.tbladdfunds.date,
dbo.tblautoipekonek.date AS Expr1
Not sure if this will work for you but this is what I would use:
SELECT
p.ipno,
p.awbno,
p.ipamount,
p.orno,
f.username,
f.newctfbal,
f.addedctfamt,
f.oldctfbal,
f.newipbal,
f.addedipamt,
f.oldipbal,
f.date,
p.date Expr1
FROM
dbo.tblautoipekonek p
CROSS APPLY
(
SELECT TOP 1
*
FROM
dbo.tbladdfunds f
WHERE
p.date = f.date
ORDER BY
f.newipbal DESC
) f
This will get you one line from each entry in dbo.tblautoipekonek joined with the matching date entry from dbo.tbladdfunds which has the highest value in newipbal