Query with table, temp table, subquery join issues - tsql

I have a query where I create 2 temp tables at the start, then I query an existing table in my DB and join this table to a subquery, and then lastly join to 1 of the temp tables. When i do this I get an error that the key I'm joining on from the existing table cannot be bound. What's weird is if I take out all references to the subquery and leave just the query with the existing table and the temp table it joins fine, and also if I joint the existing table to just the sub suery it works just fine.
But when I try to put all 3 together it gives the me the "the multi-part identified z.[currnecy key] cannot be bound", which seems an odd error since this key is in an existing table and joins just fine to a temp table or sub query alone, but not both together.
I know about issues with joining on sub queries, but in this situation it seems that the issue seems to be with joining to sub queries and temp tables in the same query, which I'm not sure how to work around.
Code is below.
declare #tmpFx table (currency_key int, effective_date_key int, expiration_date_key int, to_usd float, from_usd float ) --primary key (currency_key, date_key))
insert into #tmpFx(currency_key, effective_date_key, expiration_date_key, to_usd, from_usd)
select [currency key], cast(convert(char(8),[effective date],112) as int), cast(convert(char(8),[expiration date],112) as int), [to usd], [from usd]
from v_fx --where [effective date] >= #beginDate
declare #fixedFx table (currency_key int, to_usd float, from_usd float primary key (currency_key))
insert into #fixedFx(currency_key, to_usd, from_usd)
select [currency key], [to usd], [from usd]
from v_fx where [effective date] = '2012-01-01'
select z.[currency key], --stat_fx.to_usd to_usd, stat_fx.from_usd from_usd, --q.*,--
stat_usd_amt2 = case when z.[currency key] = 100001 then q.orig_amt else 0 end --sum(q.orig_amt * stat_fx.to_usd)
from [dim country] z,
(select b.country_key, a.currency_key, a.data_type_key, sum(a.amount) orig_amt,
sum(a.amount * stat_fx.to_usd) stat_usd_amt,
sum((a.amount * stat_fx.to_usd) * stat_fx.from_usd) home_curr_amt
from tbl_cohort a
inner join tbl_management_code b on a.management_code = b.management_code
left outer join #tmpFx stat_fx on a.currency_key = stat_fx.currency_key
where a.data_type_key = 1
and a.date_key > 20111231
group by b.country_key, a.currency_key, a.data_type_key) q
inner join #tmpFx stat_fx on z.[currency key] = stat_fx.currency_key
where q.[country_key]= z.[country key]

I believe it is because you are mixing join formats between the old style and new style (as ninesided suggested). I was able to mock up a similar issue with my own data and got the same error about unbound identifiers. Give the following a try instead.
declare #tmpFx table (currency_key int, effective_date_key int, expiration_date_key int, to_usd float, from_usd float)
insert into #tmpFx(currency_key, effective_date_key, expiration_date_key, to_usd, from_usd)
select [currency key], cast(convert(char(8),[effective date],112) as int), cast(convert(char(8),[expiration date],112) as int), [to usd], [from usd]
from v_fx
declare #fixedFx table (currency_key int, to_usd float, from_usd float primary key (currency_key))
insert into #fixedFx(currency_key, to_usd, from_usd)
select [currency key], [to usd], [from usd]
from v_fx where [effective date] = '2012-01-01'
select z.[currency key],
case when z.[currency key] = 100001 then q.orig_amt else 0 end AS stat_usd_amt2
from [dim country] z
JOIN (
select b.country_key, a.currency_key, a.data_type_key, sum(a.amount) AS orig_amt,
sum(a.amount * stat_fx.to_usd) as stat_usd_amt,
sum((a.amount * stat_fx.to_usd) * stat_fx.from_usd) as home_curr_amt
from tbl_cohort a
join tbl_management_code b
on a.management_code = b.management_code
left join #tmpFx stat_fx
on a.currency_key = stat_fx.currency_key
where a.data_type_key = 1
and a.date_key > 20111231
group by b.country_key, a.currency_key, a.data_type_key
) q
ON q.[country_key] = z.[country_key]
join #tmpFx stat_fx
on z.[currency key] = stat_fx.currency_key
And while I've left in your second temp table (#fixedFx), you may want to remove it if you don't have plans on using its data at all.

Related

how to get last added record for a battery with left join PSQL

I have query such as
select * from batteries as b ORDER BY inserted_at desc
which gives me data such as
and I have an query such as
select voltage, datetime, battery_id from battery_readings ORDER BY inserted_at desc limit 1
which returns data as
I want to combine both 2 above queries, so in one go, I can have each battery details as well as its last added voltage and datetime from battery_readings.
Postgres has a very useful syntax for this, called DISTINCT ON. This is different from plain DISTINCT in that it keeps only the first row of each set, defined by the sort order. In your case, it would be something like this:
SELECT DISTINCT ON (b.id)
b.id,
b.name,
b.source_url,
b.active,
b.user_id,
b.inserted_at,
b.updated_at,
v.voltage,
v.datetime
FROM battery b
JOIN battery_voltage v ON (b.id = v.battery_id)
ORDER BY b.id, v.datetime desc;
I think that widowing will make what you expected.
Assuming two tables
create table battery (id int, name text);
create table bat_volt(measure_time int, battery_id int, val int);
One of the possible queries is like this:
with latest as (select battery_id, max(measure_time) over (partition by battery_id) from bat_volt)
select * from battery b join bat_volt bv on bv.battery_id=b.id where (b.id,bv.measure_time) in (select * from latest);
If you have Postgres version which supports lateral, it might also make sense to try it out (in case there are way more values than batteries, it could have better performance).
select * from battery b
join bat_volt bv on bv.battery_id=b.id
join lateral
(select battery_id, max(measure_time) over (partition by battery_id) from bat_volt bbv
where bbv.battery_id = b.id limit 1) lbb on (lbb.max = bv.measure_time AND lbb.battery_id = b.id);

T-SQL how to join with one column a string and one an integer

How to join with one column a string and one an integer?
--PEOPLE_ID 000092437, PersonID 92437
select PC.PEOPLE_ID, Idn.PersonId,'Home Row 1', PC.Phone1 from #NextIdentityID Idn INNER JOIN PEOPLECHANGES PC on Idn.People_ID = PC.People_ID --PEOPLE_ID 000092437, PersonID 92437 one is varchar, one is integer
union all select PC.PEOPLE_ID, Idn.PersonId,'Office Row 2', PC.Phone2 from #NextIdentityID Idn INNER JOIN PEOPLECHANGES PC on Idn.People_ID = PC.People_ID
union all select PC.PEOPLE_ID, Idn.PersonId,'Cell Row 3', PC.Phone3 from #NextIdentityID Idn INNER JOIN PEOPLECHANGES PC on Idn.People_ID = PC.People_ID
To make sure your varchar() data doesn't raise any errors you should check to see if it can be converted into an integer. One way to do this is with a case statement in the where clause. If it is not convertible then your join won't work - but at least your query can still run with out error.
This example shows how you can avoid potential errors.
create table #tempa(id int, descr varchar(50));
create table #tempb(id varchar(10), descr varchar(50));
insert into #tempa(id,descr) values (1234,'Body getta body getta');
insert into #tempb(id,descr) values ('001234','sis boom ba - rah rah rah');
insert into #tempa(id,descr) values (5678,'Weagle Weagle War Damn Eagle');
insert into #tempb(id,descr) values ('0005678','Kickem in the butt Big blue');
insert into #tempa(id,descr) values (9012,'this wont have a match');
insert into #tempb(id,descr) values ('x0912','sis boom ba');
Select a.id as a_id, b.id as b_id
,a.descr as a_descr, b.descr as b_descr
from #tempa a
left join #tempb b
on a.id = case when isnumeric(b.id) = 1 then cast(b.id as int) else 0 end
-- this one will raise an error
Select a.id as a_id, b.id as b_id
,a.descr as a_descr, b.descr as b_descr
from #tempa a
left join #tempb b
on a.id = b.id
drop table #tempa;
drop table #tempb;
If you convert the one with leading zeros to an integer you will get equal values:
SELECT CONVERT(INT, '000092437') = 92437
However, this assumes that all of your varchar column can be convert to int.
If that's not the case then you have to write a function to go the other way and add leading zeros.

How to join vertical and horizontal table together table

I have two table with one of them is vertical i.e store only key value pair with ref id from table 1. i want to join both table and dispaly key value pair as a column in select. and also perform sorting on few keys.
T1 having (id,empid,dpt)
T2 having (empid,key,value)
select
T1.*,
t21.value,
t22.value,
t23.value,
t24.value
from Table1 t1
join Table2 t21 on t1.empid = t21.empid
join Table2 t22 on t1.empid = t22.empid
join Table2 t23 on t1.empid = t23.empid
where
t21.key = 'FNAME'
and t22.key = 'LNAME'
and t23.key='AGE'
The query you demonstrate is very inefficient (another join for each additional column) and also has a potential problem: if there isn't a row in T2 for every key in the WHERE clause, the whole row is excluded.
The second problem can be avoided with LEFT [OUTER] JOIN instead of [INNER] JOIN. But don't bother, the solution to the first problem is a completely different query. "Pivot" T2 using crosstab() from the additional module tablefunc:
SELECT * FROM crosstab(
'SELECT empid, key, value FROM t2 ORDER BY 1'
, $$VALUES ('FNAME'), ('LNAME'), ('AGE')$$ -- more?
) AS ct (empid int -- use *actual* data types
, fname text
, lname text
, age text);
-- more?
Then just join to T1:
select *
from t1
JOIN (<insert query from above>) AS t2 USING (empid);
This time you may want to use [INNER] JOIN.
The USING clause conveniently removes the second instance of the empid column.
Detailed instructions:
PostgreSQL Crosstab Query

Entity Framework does not create complex type for my stored procedure?

I have written the following sql that produces a report. Im running some sql sueries and storing their result in temp tables and at the end of the sproc I union all the tables together. however when I add this sproc to EF it does not create a complex type for it and It is useless without that. how can I make EF identify the returned resultset and create a complex type.
ALTER PROC [dbo].[Report_AllMediaDetails]
(
#profileID AS INT,
#organisationID as INT,
#startDate as date,
#endDate as date
)
AS
create table #Temp_ReportData
(
ItemOrgID int,
MediaName char(100),
CountryName char(100),
ItemOverRideDate DateTime,
Rating int,
BatchNo int,
NoInBatch int,
BatchDate DateTime
)
insert into #Temp_ReportData
select
oi.ID,
mc.Name as MediaName,
lcc.Name as Country,
i.OverrideDate as Date,
oi.Rating,
b.ID BatchNo,
i.NumInBatch ItemNumberInBatch,
bah.ChangedAtUtc as BatchDate
from
ProfileResults PR
INNER JOIN Items i ON PR.ItemID = I.ID
inner join Batches b on b.ID=i.BatchID
inner join ItemOrganisations oi on i.ID=oi.ItemID
inner join Lookup_MediaChannels mc on i.MediaChannelID=mc.id
left outer join Lookup_Cities lc on lc.ID=mc.CityID
left outer join Lookup_Countries lcc on lcc.ID=mc.CountryID
inner join BatchActionHistory bah on b.ID=bah.batchid
where pr.ProfileID = #profileID
and oi.OrganisationID = #organisationID
and b.StatusID IN (6,7)
and bah.BatchActionID = 6
and i.StatusID = 2
and i.IsRelevant = 1
and i.OverrideDate between #startDate and #endDate
--Create a temp table to hold the B section ByLines
create table #Temp_ReportByLines
(
ItemOrgID int,
ByLine char(100),
BatchNo int,
NumInBatch int,
BatchDate Date,
Rating int,
ItemOverRideDate date
)
insert into #Temp_ReportByLines
select
oi.ID,
bl1.FirstName +' '+ bl1.Surname as ByLine,
b.ID,
i.NumInBatch,
bah.ChangedAtUtc,
oi.rating,
i.OverrideDate
from ProfileResults PR
INNER JOIN Items i ON PR.ItemID = I.ID
inner join Batches b on b.ID=i.BatchID
inner join ItemOrganisations oi on i.ID=oi.ItemID
left outer join ItemByLines ib on ib.ItemID=i.ID
left outer join ByLines bl1 on bl1.ID=ib.ByLineID
inner join BatchActionHistory bah on b.ID=bah.batchid
where pr.ProfileID = #profileID
and oi.OrganisationID = #organisationID
and b.StatusID IN (6,7)
and bah.BatchActionID = 6
and i.StatusID = 2
and i.IsRelevant = 1
and bl1.IsByLine=1
and i.OverrideDate between #startDate and #endDate
--Create a temp table to hold the I section issues
create table #Temp_ReportIssues
(
ItemOrgID int,
IssueNo int,
Issue char(300),
BatchNo int,
NumInBatch int,
BatchDate Date,
Rating int,
ItemOverRideDate date
)
insert into #Temp_ReportIssues
select
iss.ItemOrganisationID,
pri.Code, pri.Name,
td.BatchNo,
td.NoInBatch,
td.BatchDate,
td.Rating,
td.ItemOverRideDate
from ItemOrganisationIssues iss
inner join #Temp_ReportData td on td.itemorgid= iss.ItemOrganisationID
inner join ProjectIssues pri on pri.ID=iss.IssueID
--Create a temp table to hold the M section messages
create table #Temp_ReportMessages
(
ItemOrgID int,
MessageType char(100),
MessageNo int,
BatchNo int,
NumInBatch int,
BatchDate Date,
Rating int,
itemOverRideDate date
)
insert into #Temp_ReportMessages
select
itemorgid, lmt.Name as MessageType ,pm.NeptuneMessageID as MessageNo,
td.BatchNo,
td.NoInBatch,
td.BatchDate,
td.Rating,
td.ItemOverRideDate
from #Temp_ReportData td
left outer join ItemOrganisationMessages iom on iom.ItemOrganisationID=td.ItemOrgID
left outer join ProjectMessages pm on iom.MessageID=pm.id
left outer join Lookup_MessageTypes lmt on lmt.ID=pm.MessageTypeID
--Create a temp table to hold the S section Source
create table #Temp_ReportSources
(
ItemOrgID int,
SourceFullName char(100),
SourceType char(20),
BatchNo int,
NumInBatch int,
BatchDate Date,
Rating int,
ItemOverRideDate date
)
insert into #Temp_ReportSources
select
ios.ItemOrganisationID,
bl2.FirstName+' '+bl2.Surname as SourceFullName,
lst.name as SourceTypeDesc,
td.BatchNo,
td.NoInBatch,
td.BatchDate,
td.Rating,
td.ItemOverRideDate
from ItemOrganisationSources ios
inner join #Temp_ReportData td on td.ItemOrgID = ios.ItemOrganisationID
inner join ByLines bl2 on bl2.ID=ios.ByLineID and bl2.IsByLine=0
left outer join Lookup_SourceTypes lst on lst.ID=ios.SourceTypeID
-- Union all the temp tables into a formatted resultset
select
td.MediaName,td.CountryName,td.ItemOverRideDate, td.Rating,td.BatchNo,td.NoInBatch, td.BatchDate, '' as DataType,'' as ByLine,'' as IssueNo,'' as Issue, '' MessageNo, '' as Message,'' as SourceName,'' as SourceType from #Temp_ReportData td
union select '','',tb.ItemOverRideDate,tb.rating,tb.batchno,tb.NumInBatch,tb.batchdate,'B', tb.ByLine,'','','','','','' from #Temp_ReportByLines tb
union select '','',tr.ItemOverRideDate,tr.rating,tr.batchno,tr.NumInBatch,tr.batchdate,'I','',tr.issueno,tr.issue,'','','', '' from #Temp_ReportIssues tr
union select '','',tm.ItemOverRideDate,tm.rating,tm.batchno,tm.NumInBatch,tm.batchdate,'M','','','',tm.messageno,tm.messagetype,'','' from #Temp_ReportMessages tm
union select '','',ts.ItemOverRideDate,ts.rating,ts.batchno,ts.NumInBatch,ts.batchdate,'S','','','','','',ts.sourcefullname,ts.sourcetype from #Temp_ReportSources ts
order by td.NoInBatch,td.BatchNo, datatype
EF will not create a complex type for you, but EF can instantiate a collection of objects of declared complex type.
for example
public class AType {
public String Prop1 {get; set;}
}
context.Database.SqlQuery<AType>("select c1 as Prop1 from ATable");
will return a collection of AType. The same if you replace your query with a call to a stored procedure.
In you case you have to declare a type for mapping the return of your stored procedure.
Otherwise you may look around dynamic but :
Don't know if it works with EF
you will loose the advantages of using a compiled language.
----- EDITION
after some test with dynamic, a correct number or object is returned, but I just can't do anything with them.

Stored Procedure Union All issue

I have a stored procedure where I am trying to combine two different SELECT statements using UNION ALL, but after the query is executed only the items from the first SELECT are returned.
I wanted to get all items from both the first and second select statements.
Here is what I have:
CREATE PROCEDURE [dbo].[Report_AllActivitiesOfficesAll]
#BeginDate datetime,
#EndDate datetime
AS
SET #BeginDate = .dbo.DateOnly(#BeginDate)
SET #EndDate = .dbo.DateOnly(#EndDate)
BEGIN
SET NOCOUNT ON;
SELECT
O.OfficeId,
O.OfficeName AS Name,
AT.Description AS Activity,
SUM(A.Duration) AS [Minutes],
CAST(SUM(A.Duration) AS FLOAT) / 60 AS [Hours],
COUNT(A.ActivityId) AS Activities,
COUNT(DISTINCT A.CaseId) AS Cases,
MIN(CAST(A.Duration AS FLOAT) / 60) AS [Min Time],
MAX(CAST(A.Duration AS FLOAT) / 60) AS [Max Time],
SUM(CAST(A.Duration AS FLOAT) / 60) / COUNT(A.ActivityId) AS [Avg Time],
SUM(CAST(A.Duration AS FLOAT) / 60) AS [TotalHours]
FROM Activity A
INNER JOIN ActivityType AT ON A.ActivityTypeId = AT.ActivityTypeId
INNER JOIN ActivityEntry AE ON A.ActivityEntryId = AE.ActivityEntryId
INNER JOIN [Case] C ON A.CaseId = C.CaseId
INNER JOIN [Office] O ON AE.OfficeId = O.OfficeId
INNER JOIN [User] U ON C.CreatedByUserId = U.UserId
WHERE .dbo.DateOnly(AE.ActivityDate) BETWEEN #BeginDate AND #EndDate
GROUP BY
O.OfficeId,
O.OfficeName,
AT.Description
UNION ALL
SELECT
O.OfficeId,
O.OfficeId AS NonCaseOfficeId,
O.OfficeName AS OfficeName,
NCAT.Description AS NonCaseActivityType,
SUM(NCA.Duration) AS [Minutes],
CAST(SUM(NCA.Duration) AS FLOAT) / 60 AS [Hours],
COUNT(NCA.NonCaseActivityId) AS Activities,
MIN(CAST(NCA.Duration AS FLOAT) / 60) AS [Min Time],
MAX(CAST(NCA.Duration AS FLOAT) / 60) AS [Max Time],
SUM(CAST(NCA.Duration AS FLOAT) / 60) / COUNT(NCA.NonCaseActivityId) AS [Avg Time],
SUM(CAST(NCA.Duration AS FLOAT) / 60) AS [TotalHours]
FROM NonCaseActivity NCA
INNER JOIN NonCaseActivityType NCAT ON NCA.NonCaseActivityTypeId = NCAT.NonCaseActivityTypeId
INNER JOIN [Office] O ON NCA.OfficeId = O.OfficeId
INNER JOIN [User] U ON NCA.UserId = U.UserId
WHERE .dbo.DateOnly(NCA.ActivityDate) BETWEEN #BeginDate AND #EndDate
GROUP BY
O.OfficeId,
O.OfficeName,
NCAT.Description
END
Columns must match in data type and content to correctly and effectively use a union. You cannot change column names in the middle. If you need to know which part of the union a record came from add another column to do so. Yours do not. Officename is in the 2nd column in the first one and the third column in the second query. What is in the second column of the second query is highly likely not to be the same datatype as the second column of the first one. This is not the only mismatch but an example.
If you need a column in the second query that you don't need in the first, you must still put it in the first query. And if you need a column in the first that you don't need in the second you must put a null value in that place in the query. For example (not a complete rewrite of what you have but enough to get an idea of what I am talking about):
SELECT
O.OfficeId,
CAST(NULL as int) as NonCaseOfficeId
O.OfficeName AS Name,
AT.Description AS Activity,
COUNT(DISTINCT A.CaseId) AS Cases,
cast('Case' as varchar (10)) as recordType
FROM Activity A
INNER JOIN ActivityType AT ON A.ActivityTypeId = AT.ActivityTypeId
INNER JOIN ActivityEntry AE ON A.ActivityEntryId = AE.ActivityEntryId
INNER JOIN [Case] C ON A.CaseId = C.CaseId
INNER JOIN [Office] O ON AE.OfficeId = O.OfficeId
INNER JOIN [User] U ON C.CreatedByUserId = U.UserId
WHERE .dbo.DateOnly(AE.ActivityDate) BETWEEN #BeginDate AND #EndDate
GROUP BY
O.OfficeId,
O.OfficeName,
AT.Description
UNION ALL
SELECT
O.OfficeId,
O.OfficeId,
O.OfficeName,
NCAT.Description,
cast(NULL as int),
'NonCase'
FROM NonCaseActivity NCA
INNER JOIN NonCaseActivityType NCAT
ON NCA.NonCaseActivityTypeId = NCAT.NonCaseActivityTypeId
INNER JOIN [Office] O ON NCA.OfficeId = O.OfficeId
INNER JOIN [User] U ON NCA.UserId = U.UserId
WHERE .dbo.DateOnly(NCA.ActivityDate) BETWEEN #BeginDate AND #EndDate
Now since the query also takes the datatype from the first query, you will see that I specifically cast it to the datatype I wanted. You may need to do that on the null in the second part of the union as well just be sure that the datatypes are matching. I believe null is assumed by SQL server to be an int if you don't specify, so this is most important when you want some other data type.
Seems fine to me. Keep in mind, the column names used will come from the first query.
So if I used a query like:
SELECT 1 AS Something
UNION ALL
SELECT 2 AS SomethingElse
I'd see just results (1 and 2) under a field called Something and wouldn't see anything for a field called SomethingElse.
Perhaps that's the issue?
Try adding an extra an extra field to both queries that identifies which resultset it's pulling from.
Maybe add 'Activity' AS Source for the first query and 'Non-Case Activity' AS Source for the second query, just to make sure you're getting a clear view of what's being shown?