Using Multiple Columns in a SQL Subquery - sql

My setup is I have two tables, Study and Activity_History. Activities run on studies so there is a 1:many relationship.
I want to be able to run a SQL query on an Activity_History table which will get me the activity and the previously run activity. I currently have this:
SELECT
*
FROM Activity_History AS A1
LEFT JOIN Activity_History AS A2
ON A2.Parent_Study_ID =
(
SELECT TOP 1 Parent_Study_ID
FROM Activity_History AS A3
WHERE A3.Parent_Study_ID = A1.Parent_Study_ID
AND A3.Activity_Date < A1.Activity_Date
ORDER BY Activity_Date DESC
)
This is not working. What's happening is its pulling the Activity_Date party of the query has no effect and it just returns the first matching Activity_Date in descending date order for every row. I think this is happening because in my subquery I am using Activity_Date in the where, but this is not in the subquery select.
Thanks for any help!

I'm assuming you're using SQL Server? If so, then this should work using ROW_NUMBER():
WITH CTE AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY Parent_Study_Id ORDER BY Activity_Date ) RN
FROM Activity_History
)
SELECT *
FROM CTE T1
LEFT JOIN CTE T2 ON T1.RN = T2.RN+1 AND T1.Parent_Study_Id = T2.Parent_Study_Id
And here is the SQL Fiddle.

In SQLServer2005+ instead LEFT JOIN you need to use OUTER APPLY
SELECT *
FROM Activity_History AS A1 OUTER APPLY (
SELECT TOP 1 Parent_Study_ID
FROM Activity_History AS A2
WHERE A2.Parent_Study_ID = A1.Parent_Study_ID
AND A2.Activity_Date < A1.Activity_Date
ORDER BY A2.Activity_Date DESC
) o

Related

SQL Server query showing most recent distinct data

I am trying to build a SQL query to recover only the most young record of a table (it has a Timestamp column already) where the item by which I want to filter appears several times, as shown in my table example:
.
Basically, I have a table1 with Id, Millis, fkName and Price, and a table2 with Id and Name.
In table1, items can appear several times with the same fkName.
What I need to achieve is building up a single query where I can list the last record for every fkName, so that I can get the most actual price for every item.
What I have tried so far is a query with
SELECT DISTINCT [table1].[Millis], [table2].[Name], [table1].[Price]
FROM [table1]
JOIN [table2] ON [table2].[Id] = [table1].[fkName]
ORDER BY [table2].[Name]
But I don't get the correct listing.
Any advice on this? Thanks in advance,
A simple and portable approach to this greatest-n-per-group problem is to filter with a subquery:
select t1.millis, t2.name, t1.price
from table1 t1
inner join table2 t2 on t2.id = t1.fkName
where t1.millis = (select max(t11.millis) from table1 t11 where t11.fkName = t1.fkName)
order by t1.millis desc
using Common Table Expression:
;with [LastPrice] as (
select [Millis], [Price], ROW_NUMBER() over (Partition by [fkName] order by [Millis] desc) rn
from [table1]
)
SELECT DISTINCT [LastPrice].[Millis],[table2].[Name],[LastPrice].[Price]
FROM [LastPrice]
JOIN [table2] ON [table2].[Id] = [LastPrice].[fkName]
WHERE [LastPrice].rn = 1
ORDER BY [table2].[Name]

SQL Server Join - With INFO_SCHEMA information

I have the first table:
select COLUMN_NAME
from Emerald_Data.INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME = N'tbl_Client_List_Pricing'
Don't mind the numbering in the Column_Name. I was doing this while testing because I need the order to remain as they are in the table. Not by ASC, DESC.
Anyhow, I don't know how to use the row numbers on the left that the system provides to JOIN another table without a condition.
Here is Table 2:
You can see that the left row numbers are my linking value but I don't know how to use that system index value as a condition in my JOIN.
Or if there is another way to join these two tables without a condition while keeping the Table 1 information in it's correct position and not affecting it by ORDER would be much appreciated.
Thank you!
-Chase
I guess you are looking for row_number. Use row_number to order result of two queries then join by matching order nums. Your query would be something like
with query_1 as (
select COLUMN_NAME
, rn = row_number() over (order by cast(left(COLUMN_NAME, 3) as int))
from Emerald_Data.INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME = N'tbl_Client_List_Pricing'
)
, query_2 as (
select
*, rn = row_number() over (order by (select null))
from
Table_2
)
select
*
from
query_1 q1
join query_2 q2 on q1.rn = q2.rn
select COLUMN_NAME from Emerald_Data.INFORMATION_SCHEMA.COLUMNS
inner join with Table_2 on Num=cast(LEFT(COLUMN_NAME,CHARINDEX('-', COLUMN_NAME)) AS int)
where TABLE_NAME = N'tbl_Client_List_Pricing'
You could also use sys.all_columns object which could able to state the index for your desired column & JOIN them with table2
SELECT *
FROM sys.all_columns c
INNER JOIN Table2 t ON t.Num = c.column_id
WHERE OBJECT_NAME(object_id) = 'tbl_Client_List_Pricing'

Only one expression can be specified in the select list when the subquery is not introduced with EXISTS. in subquery sqlserver

I want to execute this query in my database.As you can see both tables A and B has one-many relations ,but i need the latest record in B.so i here is my query :
select *,(select top 1 ResultTest ,ResultState2 from B where GasReceptionId=A.Id order by Id desc)
from A where OrganizationGasId= 4212
But i get this error
Msg 116, Level 16, State 1, Line 2
Only one expression can be specified in the select list when the subquery is not introduced with EXISTS.
You can rephrase this query as a basic join which uses an analytic function (e.g. row number) to identify the correct row's data from B to include with each record coming from the A table.
SELECT *
FROM
(
SELECT a.*, b.ResultTest, b.ResultState2,
ROW_NUMBER() OVER (PARTITION BY a.Id ORDER BY a.ID DESC) rn
FROM A a
LEFT JOIN B b
ON a.Id = b.GasReceptionId
WHERE
a.OrganizationGasId = 4212
) t
WHERE t.rn = 1;
A subquery in the SELECT clause must return exactly one column (and one or zero rows). So you can either have two subqueries:
select
a.*,
(select top 1 resulttest from b where gasreceptionid = a.id order by id desc) as test,
(select top 1 resultstate2 from b where gasreceptionid = a.id order by id desc) as state
from a
where a.organizationgasid = 4212;
Or, much better, move the subquery to the FROM clause. One way is OUTER APPLY:
select
a.*, r.resulttest, r.resultstate2
from a
outer apply
(
select top 1 resulttest, resultstate2
from b
where gasreceptionid = a.id
order by id desc
) r
where a.organizationgasid = 4212;

SQL Server ROW_NUMBER Left Join + when you don't know column names

I'm writing a page that will create a query (for non-db users) and it create the query and run it returning the results for them.
I am using row_number to handle custom pagination.
How do I do a left join and a row_number in a subquery when I don't know the specific columns I need to return. I tried to use * but I get an error that
The column '' was specified multiple times
Here is the query I tried:
SELECT * FROM
(SELECT ROW_NUMBER() OVER (ORDER BY Test) AS ROW_NUMBER, *
FROM table1 a
LEFT JOIN table2 b
ON a.ID = b.ID) x
WHERE ROW_NUMBER BETWEEN 1 AND 50
Your query is going to fail in SQL Server regardless of the row_number() call. The * returns all columns, including a.id and b.id. These both have the same name. This is fine for a query, but for a subquery, all columns need distinct names.
You can use row_number() for an arbitrary ordering by using a "subquery with constant" in the order by clause:
SELECT * FROM
(SELECT ROW_NUMBER() OVER (ORDER BY (select NULL)) AS ROW_NUMBER, *
FROM table1 a
LEFT JOIN table2 b
ON a.ID = b.ID) x
WHERE ROW_NUMBER BETWEEN 1 AND 50 ;
This removes the dependency on the underlying column name (assuming none are named ROW_NUMBER).
Try this sql. It should work.
SELECT * FROM
(SELECT ROW_NUMBER() OVER (ORDER BY a.Test) AS ROW_NUMBER, a.*,b.*
FROM table1 a
LEFT JOIN table2 b
ON a.ID = b.ID) x
WHERE ROW_NUMBER BETWEEN 1 AND 50

Where Statement w/ Distinct

I have a large table but for the purposes of this question, let's assume I have the follwoing column strucure:
I'd like to have a Where statement that returns only rows where the e-mail address is distinct in that particular column.
Thoughts?
SELECT BillingEMail
FROM tableName
GROUP BY BillingEMail
HAVING COUNT(BillingEMail) = 1
OR HAVING COUNT(*) = 1
SQLFiddle Demo
I don't know what RDBMS you are using (the reason why i can't introduce of using analytical functions) but you can do this by joining with a subquery if you want to get all columns
SELECT a.*
FROM tableName a
INNER JOIN
(
SELECT BillingEMail
FROM tableName
GROUP BY BillingEMail
HAVING COUNT(BillingEMail) = 1
)b ON a.BillingEMail = b.BillingEMail
SQLFIddle Demo
In most databases, you can do this
select t.AccountId, t.BillingEmail
from (select t.*, count(*) over (partition by BillingEmail) as cnt
from t
) t
where cnt = 1
The advantage of this approach is that you can get as many columns as you like from the table.
I prefer JW's approach, but here is another one using NOT EXISTS.
SELECT AccountID, [Billing Email]
FROM table t1
WHERE NOT EXISTS (
-- Make sure that no other row contains the same
-- email, but a different Account ID.
SELECT 1
FROM table t2
WHERE t1.[Billing Email] = t2.[Billing Email]
AND t1.AccountID <> t2.AccountID
)