SQL pagination using INNER JOINs and filtering with LIKE - sql

This query feeds a data table with sorting, filtering, and pagination. All features worked fine until I added the INNER JOIN and then i got:
The multi-part 'identifier "Types.Description" could not be bound
if i remove the second WHERE clause at the end of the query the LIKE statements work, but i lose pagination. I removed some of the LIKE clauses to try and clean up this monstrous query.
SELECT *
FROM (
SELECT ROW_NUMBER() OVER (ORDER BY TAG asc) AS RowNumber, *
FROM (
SELECT (SELECT COUNT(*) FROM Instruments) AS TotalDisplayRows, (SELECT COUNT(*) FROM Instruments) AS TotalRows, Instruments.Tag, Instruments.Location, Instruments.Description, Types.Description As TypeDesc, Manufacturer.Name, Lease.Name as LeaseName, Facility.Name as FacName
FROM Instruments
INNER JOIN Types ON Instruments.Type = Types.ID
INNER JOIN Manufacturer ON Instruments.Manufacturer = Manufacturer.ID
INNER JOIN Facility ON Instruments.Facility = Facility.ID
INNER JOIN Lease ON Instruments.Lease = Lease.ID
WHERE (Types.Description LIKE '%Cat%')
) RawResults
) Results
WHERE (Types.Description LIKE '%Cat%') AND RowNumber BETWEEN 1 AND 10

I think this is your problem
WHERE (types.description LIKE '%Cat%')
You can't do this because you are actually selecting from your derived table named Results and you aliased the column as TypeDesc.
So it should be
WHERE (results.typeDesc LIKE '%Cat%')

Related

What is causing the multi-part join error in this SQL?

I have been struggling with the sql and have tried a few approaches but can't get it working.
Can any SQL experts work out why this SQL is erroring, I think it's due to the ORDER BY?
SELECT
t.*
FROM
(SELECT
ROW_NUMBER() OVER (ORDER BY [owner_details].[id]) AS _row_num,
COUNT(count_column)
FROM
(SELECT 1 AS count_column
FROM [owner_details]
LEFT OUTER JOIN currencies cur ON owner_details.currency_id = cur.id
LEFT OUTER JOIN primary_contacts as pc ON owner_details.primary_contact_id = pc.id
LEFT OUTER JOIN contacts ON pc.contact_id = contacts.id
WHERE [owner_details].[primary_id] = 405062121) subquery_for_count) AS t
WHERE
t._row_num BETWEEN 1 AND 20
I should note that this SQL is programmatically generated via an ORM in Ruby on Rails but if I can work out the issue with the SQL maybe I can figure out how to change my code.
I want to understand the SQL better.
The error:
The multi-part identifier "owner_details.id" could not be bound..
try like below using t.[id] in order by i have assumed you id column in owner_details
SELECT
t.*
FROM
(SELECT
ROW_NUMBER() OVER (ORDER BY subquery_for_count.[id]) AS _row_num,
COUNT(count_column) over()
FROM
(SELECT 1 AS count_column,owner_details.id as id
FROM [owner_details]
LEFT OUTER JOIN currencies cur ON owner_details.currency_id = cur.id
LEFT OUTER JOIN primary_contacts as pc ON owner_details.primary_contact_id = pc.id
LEFT OUTER JOIN contacts ON pc.contact_id = contacts.id
WHERE [owner_details].[primary_id] = 405062121
) subquery_for_count) AS t
WHERE
t._row_num BETWEEN 1 AND 20
The multi-part naming conversion basically goes four levels (other than columns) in SQL. i.e.
Object (Table, view, stored procedure etc..)
Schema (schema name, default 'dbo')
Database (Database name)
Server (Server name, particularly when querying linked servers)
In your case [owner_details] is considered as table in sub-query, and as schema in OVER clause. Aside from this since you add alias name to sub-query (subquery_for_count), you should call it as subquery_for_count.id (in OVER clause)
Full query must go as:
SELECT
t.*
FROM
(SELECT
ROW_NUMBER() OVER (ORDER BY subquery_for_count.[id]) AS _row_num,
COUNT(count_column) as CountCoumn
FROM
(SELECT 1 AS count_column, owner_details.id as id
FROM [owner_details]
LEFT OUTER JOIN currencies cur ON owner_details.currency_id = cur.id
LEFT OUTER JOIN primary_contacts as pc ON owner_details.primary_contact_id = pc.id
LEFT OUTER JOIN contacts ON pc.contact_id = contacts.id
WHERE [owner_details].[primary_id] = 405062121
) subquery_for_count
GROUP BY subquery_for_count.[id]
) AS t
WHERE
t._row_num BETWEEN 1 AND 20

Can we select first row of data from column in sql?

I have a table with multiple data for same ID. I want to get the first row data for the ID.
I have added the below SQL that I have tried.
SELECT
"client"."id",
"client"."company_name",
"client_details"."address"
from Client
LEFT OUTER JOIN "client_details" ON ("client"."id" = "client_details"."client_id")
Since I have multiple address for the same ID, can we get only the first id?
Currently the output I get is 2 rows with different addresses.
You can add to your SQL LIMIT 1 and in case you want to be sure the order you can also add to your SQL ORDER BY...
You can use distinct on:
select distinct on (c.id) c.id, c.company_name, cd.address
from Client c left join
client_details cd
on c.id = cd.client_id
order by c.id, ?;
The ? is for the column that specifies the ordering (the definition of "first"). I am guessing that cd.id is what you want.
Note that this query removes the double quotes and introduces table aliases. This is easier on both the eyes (to read) and the fingers (to type).
use row_number()
select * from
(
SELECT
"client"."id",
"client"."company_name",
"client_details"."address",row_number() over(partition by "client"."id" order by "client_details"."address") as rn
from Client
LEFT OUTER JOIN "client_details" ON "client"."id" = "client_details"."client_id"
)A where rn=1
If there is a field you can order the results by you could use a lateral join e.g.
SELECT
"client"."id",
"client"."company_name",
"client_details"."address"
from Client
left join lateral (
select *
from client_details cd
where cd.client_id = client.id
order by [some_ordering_field]
limit 1
) "client_details" on true

BigQuery - Joining on multiple conditions using subqueries and OR statements

Is there anyway to join two tables on multiple potential conditions?
I'm currently migrating some code from Postgres to Bigquery where I joined on multiple potential values like:
SELECT
*
FROM
(
SELECT
offer_table.offer_id
,customer_table.customer_name
,customer_table.visit_count
,ROW_NUMBER() OVER (PARTITION BY offer_table.offer_id ORDER BY customer_table.visit_count DESC) AS customer_visit_rank
FROM
offer_table
LEFT JOIN customer_table ON
(
offer_table.customer_id = customer_table.customer_id
OR offer_table.email = customer_table.email
OR offer_table.phone = customer_table.phone
)
) dummy
WHERE
customer_visit_rank = 1
I needed to this because my offer and customer data had inconsistent usage of our id, email, and phone fields but all were valid potential matches. If multiple fields worked (ex: id and email matched), there would be duplicate rows and I'd filter them out based on the row_number column after ranking using the ORDER BY section.
However when I try to join on multiple conditions in BigQuery, I get this error message:
LEFT OUTER JOIN cannot be used without a condition that is an equality of fields from both sides of the join.
Has anyone figured out a solution to join on multiple values instead of doing the above?
You can write separate queries, then use COALESCE:
SELECT
*
FROM
(
SELECT
offer_table.offer_id
,COALESCE(c1.customer_name,c2.customer_name,c3.customer_name)
,COALESCE(c1.visit_count,c2.visit_count,c3.visit_count)
,ROW_NUMBER() OVER (PARTITION BY offer_table.offer_id ORDER BY customer_table.visit_count DESC) AS customer_visit_rank
FROM
offer_table
LEFT JOIN customer_table c1
ON offer_table.customer_id = customer_table.customer_id
LEFT JOIN customer_table c2
ON offer_table.email = customer_table.email
LEFT JOIN customer_table c3
ON offer_table.phone = customer_table.phone
)
) AS dummy
WHERE
customer_visit_rank = 1

Query fast, but when in a VIEW, it's slow - due to ROW_NUMBER

I have a query that when run, it's result is instant.
However, I paste the exact same query into VIEW, and the results take 6 seconds to reply.
For example,
SELECT ... FROM MyTables WHERE PersonID = x
runs fast.
But create a view with:
SELECT ... FROM MyTables
And then call the view:
SELECT * FROM MyView WHERE PersonID = x
And it runs slow.
Actual Query:
select ROW_NUMBER() over(partition by h.Id order by h.[SysStartTime]) as VersionNUmber,
h.Id,
fac.HIC,
... plus 18 other columns from the joined tables.
from [hist].[A_View] as h
inner join [dbo].[Facilities] as fac
on fac.Id = h.FacilityId
inner join ref.FormStatus as r_fs
on r_fs.Id = h.FormStatusId
inner join TableA as data
on data.Id = h.dataId
inner join Consultants as c
on c.Id = h.ConsultantId
inner join dbo.Specialties spec
on spec.Id = h.SpecialtyId
inner join dbo.Users modifieduser
on modifieduser.Id = h.ModifiedByUserId
left join ref.ARefTable as r_uc
on r_uc.Id = h.refId
cross apply [dbo].[getPersonUrn](h.PersonId, h.AnotherIdId) as PersonURN
(Note, I am changing some table names and columns as we're in quite a confidential area)
I notice that 97% of the time, it's in a Sort (Top N Sort), when executing the view. In the query, that 34%, but the plans are completely different.
I suspected parameter sniffing, but don't think that's an issue with Views.
I've actually just 'fixed' it, but no idea why.
My first column in my select is a ROW_NUMBER.
SELECT ROW_NUMBER() over(partition by h.Id order by h.[SysStartTime]) as` VersionNumber,
Removing that, and I get instant results.
Not sure why, as both the columns I order by and partition by, are already in the result set.
1) Here ROW_NUMBER applies to filtered data only:
SELECT ROW_NUMBER(), ... FROM MyTables WHERE PersonID = x
At first it filters by PersonID, then it computes ROW_NUMBER
2) Here ROW_NUMBER applies to all of the data:
CREATE VIEW MyView as
select ROW_NUMBER(), ... FROM MyTables
SELECT * FROM MyView WHERE PersonID = x
and only after proceeding full data the filter by PersonID is applied
it's the same as
SELECT * FROM
(SELECT ROW_NUMBER(), ... FROM MyTables
) t
WHERE t.PersonID = x
check out the example:
GO
CREATE VIEW dbo.test_view
AS
SELECT ROW_NUMBER() OVER (ORDER BY NAME) rn, o.name, o.[object_id]
FROM sys.objects o
GO
SET SHOWPLAN_XML ON
GO
SELECT rn, o.name, o.[object_id] FROM dbo.test_view o
WHERE OBJECT_ID < 100
GO
SELECT ROW_NUMBER() OVER (ORDER BY NAME) rn, o.name, o.[object_id] FROM sys.objects o
WHERE OBJECT_ID < 100
GO
SET SHOWPLAN_XML OFF
GO
DROP VIEW dbo.test_view
GO
With the view filter operation is in the very end. So plans are different actually.
I found that running it in a view, it counts every record with ROW_NUMBER() then selects. When you run it in a query it just counts the records returning with ROW_NUMBER()
I found moving all my OVER clauses outside of the primary SELECT made a huge difference in performance when calling my view with a filter. If I ran my view's code by itself with a filter, it would return instant results, but when in a view with the same filter, it would take over almost two minutes to return the same results. After moving the OVERS out like below, the filtered view runs instantly now.
Old way:
CREATE VIEW as dbo.vw_SalesTotals
AS
SELECT
OrderDetails.Item
, OrderDetails.Sales
, OrderDetails.DocID
, SUM(OrderDetails.Sales) OVER (PARTITION BY OrderDetails.DocID) as TotalDocSales
FROM dbo.OrderDetails
New way:
CREATE VIEW as dbo.vw_SalesTotals
AS
SELECT
Ord.Item
, Ord.Sales
, Ord.DocID
, SUM(Ord.Sales) OVER (PARTITION BY Ord.DocID) as TotalDocSales
FROM (
SELECT
OrderDetails.Item
, OrderDetails.Sales
, OrderDetails.DocID
FROM dbo.OrderDetails
) Ord

i want to modify this SQL statement to return only distinct rows of a column

select
picks.`fbid`,
picks.`time`,
categories.`name` as cname,
options.`name` as oname,
users.`name`
from
picks
left join categories
on (categories.`id` = picks.`cid`)
left join options
on (options.`id` = picks.oid)
left join users
on (users.fbid = picks.`fbid`)
order by
time desc
that query returns a result that like:
my question is.... I would like to modify the query to select only DISTINCT fbid's. (perhaps the first row only sorted by time)
can someone help with this?
select
p2.fbid,
p2.time,
c.`name` as cname,
o.`name` as oname,
u.`name`
from
( select p1.fbid,
min( p1.time ) FirstTimePerID
from picks p1
group by p1.fbid ) as FirstPerID
JOIN Picks p2
on FirstPerID.fbid = p2.fbid
AND FirstPerID.FirstTimePerID = p2.time
LEFT JOIN Categories c
on p2.cid = c.id
LEFT JOIN Options o
on p2.oid = o.id
LEFT JOIN Users u
on p2.fbid = u.fbid
order by
time desc
I don't know why you originally had LEFT JOINs, as it appears that all picks must be associated with a valid category, option and user... I would then remove the left, and change them to INNER joins instead.
The first inner query grabs for each fbid, the FIRST entry time which will result in a single entity for the FBID. From that, it re-joins to the picks table for the same ID and timeslot... then continues for the rest of the category, options, users join criteria of that single entry.
2 options, you could write a group by clause.
Or you could write a nested query joined back to itself to get pertinent info.
Nested aliased table:
SELECT
n.fBids
FROM
MyTable t
INNER JOIN
(SELECT DISTINCT fBids
FROM MyTable) n
ON n.ID = t.ID
Or group by option
SELECT fBId from MyTable
GROUP BY fBID
select picks.`fbid`, picks.`time`, categories.`name` as cname,
options.`name` as oname, users.`name` from picks left join categories
on (categories.`id` = picks.`cid`) left join options on (options.`id` = picks.oid)
left join users on (users.fbid = picks.`fbid`)
order by time desc GROUP BY picks.`fbid`
select
picks.fbid,
MIN(picks.time) as first_time,
MAX(picks.time) as last_time
from
picks
group by
picks.fbid
order by
MIN(picks.time) desc
However, if you want only distinct fbid's you cannot display cname and other columns at the same time.