Two Queries on Same Table Columns? - sql

I'm trying to build a query that will take specific criteria and split it into new columns on the table. For example, below I have the raw data when I do this query:
select Warehouse
, StockCode
, QtyOnHand
, QtyAllocated
, SalesQty1
, SalesQty2
, SalesQty3
from InvWarehouse
Warehouse
StockCode
QtyOnHand
QtyAllocated
SalesQty1
SalesQty2
SalesQty3
OF
ACN2-1015-2.3
36900.000000
0.000000
0.000000
0.000000
0.000000
01
ACN2-1015-2.3
22475.000000
0.000000
0.000000
0.000000
0.000000
OF
ACN2-8125-1.9
108000.000000
0.000000
0.000000
0.000000
0.000000
01
ACN2-8125-1.9
45600.000000
0.000000
0.000000
0.000000
0.000000
OF
CA-2520S-151ZY
74632.000000
0.000000
0.000000
0.000000
0.000000
Some of these parts are in both OF and 01 Warehouses and what I want to do is compare the OF QtyOnHands to the 01 QtyOnHands.
I built this query, but the qtyonhands seem to be wrong so I must be doing something incorrectly. How do I correct this?
select
WOF.Warehouse
, WOF.StockCode
, WOF.QtyOnHand
, W01.QtyOnHand
, W01.QtyAllocated
, W01.[SalesQty1]
, W01.[SalesQty2]
, W01.[SalesQty3]
from InvWarehouse W01
left join InvWarehouse WOF on WOF.Warehouse = W01.Warehouse
where W01.QtyOnHand > '0'
and WOF.Warehouse = 'OF'
As said before, I'm trying to split the data to get two columns for Quantity On Hand. One for OF and one for 01. But I ONLY want to show OF in the warehouse column. So something like the below:
Warehouse
StockCode
W01.QtyOnHand
WOF.QtyOnHand
W01.QtyAllocated
WOF.SalesQty1
WOF.SalesQty2
WOF.SalesQty3
OF
ACN2-1015-2.3
36900.000000
36900.000000
0.000000
0.000000
0.000000
0.000000
OF
ACN2-8125-1.9
108000.000000
36900.000000
0.000000
0.000000
0.000000
0.000000
Let me know if further clarification is needed, but bottom line is I want to pull the data from Warehouse = 01 and also Warehouse = OF and be able to add the QtyOnHand 01 onto my table that has QtyOnHand OF. So basically two queries where one shows Warehouse 01 and the other shows Warehouse OF, combined into one query that I can grab specific info from.

This will not match your expected output exactly. As the comments suggest, I don't think you've described your problem clearly (I don't understand why you would have W01.QtyOnHand as 36900).
However, I suspect you are looking for something like:
select
WOF.Warehouse
, W01.StockCode
, WOF.QtyOnHand
, W01.QtyOnHand
, W01.QtyAllocated
, W01.[SalesQty1]
, W01.[SalesQty2]
, W01.[SalesQty3]
from InvWarehouse W01
left join InvWarehouse WOF on WOF.StockCode = W01.StockCode
where W01.QtyOnHand > 0
and WOF.Warehouse = 'OF'
and W01.Warehouse = '01'
Note the join here is ON WOF.StockCode = W01.StockCode (not ON WOF.Warehouse = W01.Warehouse) and I've added a new condition and W01.Warehouse = '01'. All together, this will give you two rows as you expect and the WOF.* columns in your SELECT will refer to the warehouse='OF' row for each StockCode and the W01.* columns in your SELECT will refer to the warehouse='01' row for each StockCode.

Related

Access SQL Crosstab Function SELECT TOP 1

I am calculating waste based on an administered amount corrected using a correction factor read from a table. The structure of the tables with some sample data is below:
RMP Administrations:
Nuclide Product MBq Date Given
-------------------------------------------------
Tc-99m Pertechnetate 700 2018/01/01
I-131 NaI 399 2018/02/01
I-131 NaI 555 2018/01/01
I-123 MIBG 181 2018/01/01
I-123 NaI 29 2018/01/03
WasteFactors
Nuclide Product MinActivity MaxActivity Factor
------------------------------------------------------------
Tc-99m * 0.3
I-123 * 150 0.3
I-123 * 150 1
I-123 MIBG 0.6
I-131 * 400 0.5
I-131 * 400 1
So this table is complex, but it is the best way I can think of to represent the correction factors in a table. Nuclide is matched first, then if product matches that correction factor is used, finally we check the activity (MBq) against the Min / Max columns to decide. We then use this factor along with the activity to determine the waste using the following SQL
SELECT
Nuclide,
[Date Given] AS Given,
(SELECT TOP 1
Factor
FROM WasteFactors
WHERE [RMP Administrations].Nuclide = WasteFactors.Nuclide
AND [RMP Administrations].Product LIKE WasteFactors.Product
AND (WasteFactors.MinActivity IS NULL
OR WasteFactors.MinActivity > [RMP Administrations].MBq)
AND (WasteFactors.MaxActivity IS NULL
OR WasteFactors.MaxActivity <= [RMP Administrations].MBq)
ORDER BY WasteFactors.Nuclide ASC, WasteFactors.Product DESC)
AS Waste
FROM [RMP Administrations] WHERE NOT [RMP Administrations].Nuclide IS NULL AND NOT [RMP Administrations].MBq IS NULL
So this achieves what we require by sorting the Factor table so that factors with a product name appear before factors which apply to all other products, so with the data above 'I-123 MIBG' is checked before 'I-123 *'.
so, running this SQL along with the data above should return the following:
Nuclide Given Waste
--------------------------------------------------------
Tc-99m 2018/01/01 0.3 (All Tc-99m is 0.3)
I-131 2018/02/01 1 (Activity <=400)
I-131 2018/01/01 0.5 (Activity >400)
I-123 2018/01/01 0.6 (Product is MIBG)
I-123 2018/01/03 0.3 (Not MIBG, <150)
with that factor used as MBq * (SELECT TOP 1...) AS Waste in the real code. So... this works fine; and I can summarise by data annually with a normal SUM(Waste), GROUP BY Nuclide and WHERE Year(Given)=[Enter Year]. My issues begin when I try to use this in the following crosstab query:
PARAMETERS [Enter Year] Short;
TRANSFORM SUM(T.MBq *
(SELECT TOP 1
Factor
FROM WasteFactors
WHERE T.Nuclide = WasteFactors.Nuclide
AND T.Product LIKE WasteFactors.Product
AND (WasteFactors.MinActivity IS NULL
OR WasteFactors.MinActivity >T.MBq)
AND (WasteFactors.MaxActivity IS NULL
OR WasteFactors.MaxActivity <= T.MBq)
ORDER BY WasteFactors.Nuclide ASC, WasteFactors.Product DESC)
)
SELECT T.Nuclide
FROM [RMP Administrations] AS T
WHERE Year(T.[Date Given])=[Enter Year]
GROUP BY T.Nuclide
PIVOT Format(T.[Date Given],"mm - mmm");
giving the error 'Access does not recognise T.Nuclide as a valid field or....'. I can't see an error in my SQL, and don't see why it should not work as written, I have tried making a VBA function to calculate the waste amount SUM(GetWaste(Nuclide, Product,MBq)), with the function running the same SQL as above in a recordset however then my query is too complex to be evaluated.
Does anyone have any ideas on where I have gone wrong with my crosstab query, how I can restructure my WasteFactors to make then easier to query, or is this just too complex to try to do in SQL and I should just do it in VBA it instead?
The real data set is ~1000 records, spanning multiple months, I would like to change Table and Col names to something not crap but I didn't create the database. Expected output for above data would be:
Nuclide 01 - Jan 02 - Feb
-------------------------------
Tc-99m 210
I-131 277.5 399
I-123 117.3
You can't use a subquery in a TRANSFORM clause in that way afaik.
Use a subquery in your FROM clause multiplying that subquery with MBq. Then only use a plain aggregate in the TRANSFORM clause
Sample, probably needs refining:
PARAMETERS [Enter Year] Short;
TRANSFORM SUM(TransformField)
SELECT R.Nuclide
FROM (SELECT *,
Mbq * (SELECT TOP 1
Factor
FROM WasteFactors
WHERE T.Nuclide = WasteFactors.Nuclide
AND T.Product LIKE WasteFactors.Product
AND (WasteFactors.MinActivity IS NULL
OR WasteFactors.MinActivity >T.MBq)
AND (WasteFactors.MaxActivity IS NULL
OR WasteFactors.MaxActivity <= T.MBq)
ORDER BY WasteFactors.Nuclide ASC, WasteFactors.Product DESC) As TransformField
FROM [RMP Administrations] T
) AS R
WHERE Year(R.[Date Given])=[Enter Year]
GROUP BY R.Nuclide
PIVOT Format(R.[Date Given],"mm - mmm");

Most efficient way to combine data from different database when join is not possible

I have two tables of data and I am looking to combine them in single view for reporting purposes (excel and ssrs)
The problem is the view is taking 4 minutes to return 75 rows
Can anyone suggest a better way, I know I could set a job to create a table and update the table overnight and then source my report off the flattened table to improve speed, but is there a better way?
Here is the basic outline and sql
Table 1 is summary data from one database on the server and hold this type on data. This table has 75 rows
WageYear StartDate EndDate Week No
2013/14 16/11/2013 22/11/2013 34
2013/14 09/11/2013 15/11/2013 33
2013/14 02/11/2013 08/11/2013 32
2013/14 26/10/2013 01/11/2013 31
The second source of data is a view which sits on a selection view that has four outer joins to sub queries and it returns daily occurrence of stock movement with production data calculated - the source table has 1,090,171 rows
StockCode TrnYear TrnMonth EntryDate IntoDespKg
Part A 2013 1 06/04/2012 634.5
Part A 2013 1 23/04/2012 634.5
Part B 2013 2 03/05/2012 660
Part B 2013 2 03/05/2012 660
The view I have written combines the data and totals the IntoDespKg column so it looks like
WageYear StartDate EndDate Week No IntoDespKg TCastKg
2013/14 16/11/2013 22/11/2013 34 141170.925 173840.482
2013/14 09/11/2013 15/11/2013 33 149969.934 134483.17
2013/14 02/11/2013 08/11/2013 32 137661.513 165725.115
2013/14 26/10/2013 01/11/2013 31 137586.634 179026.199
My Sql is using sub queries
SELECT [WageYear]
,[StartDate]
,[EndDate]
,[Week No]
------------------Std Information------------------
,(Select sum ([IntoDespKg]) from [dbo].[CHCIW_RecoveryLabDirectMins]
where ([EntryDate] >=W.[StartDate] and [EntryDate] <=W.[EndDate])) As IntoDespKg
,(Select sum ([CastKg]) from [dbo].[CHCIW_RecoveryLabDirectMins]
where ([EntryDate] >=W.[StartDate] and [EntryDate] <=W.[EndDate])) As [TCastKg]
,(Select sum (CoreDirectMins)/60 from [dbo].[CHCIW_RecoveryLabDirectMins]
where ([EntryDate] >=W.[StartDate] and [EntryDate] <=W.[EndDate])) As [CoreDirectHrs]
FROM [Wages].[vwCHHoursByAreaByWeek] W
ORDER BY EndDate DESC
As Sam Yi pointed out, you can use a simple GROUP BY statement. If you are expecting date ranges without records in dbo.CHCIW_RecoveryLabDirectMins, then use a LEFT JOIN. I also re-aliased your table names.
SELECT HoursByAreaByWeek.WageYear
,HoursByAreaByWeek.StartDate
,HoursByAreaByWeek.EndDate
,HoursByAreaByWeek.[Week No]
,Sum(RecoveryLabDirectMins.IntoDespKg) As IntoDespKg
,Sum(RecoveryLabDirectMins.CastKg) As TCastKg
,Sum(CoreDirectMins / 60) As CoreDirectHrs
FROM Wages.vwCHHoursByAreaByWeek As HoursByAreaByWeek
JOIN dbo.CHCIW_RecoveryLabDirectMins As RecoveryLabDirectMins
ON RecoveryLabDirectMins.EntryDate Between HoursByAreaByWeek.StartDate AND HoursByAreaByWeek.EndDate
GROUP BY HoursByAreaByWeek.WageYear
,HoursByAreaByWeek.StartDate
,HoursByAreaByWeek.EndDate
,HoursByAreaByWeek.[Week No]
ORDER BY EndDate DESC
SELECT [WageYear]
,[StartDate]
,[EndDate]
,[Week No]
------------------Std Information------------------
,IntoDespKg
,[TCastKg]
,[CoreDirectHrs]
FROM [Wages].[vwCHHoursByAreaByWeek] W
CROSS APPLY (
SELECT
sum ([IntoDespKg]) IntoDespKg,
sum ([CastKg]) [TCastKg],
sum (CoreDirectMins)/60 [CoreDirectHrs]
from [dbo].[CHCIW_RecoveryLabDirectMins]
where ([EntryDate] >=W.[StartDate] and [EntryDate] <=W.[EndDate])
) H

SQL/sqlite query to union tables with matching columns and merged columns

I'm working with a database that has 2 tables for each company with 4 companies (8 total DBs for thisquery) and for reasons outside of my control that can't be changed. This is also an sqlite DB.
My app currently has to do 8 round trips to get all the data. I want to consolidate that down to one table view query but I can't figure out how to combined the data in a way that would make it work. Here is an example of the tables.
Table 1 (Type A)
name zone
ABCD ABC1
DBAA CBA1
Table 2 (Type A)
name zone
ABCD 1234
DBAA 4321
Table 1 (Type B)
zone weight rate
ABC1 1 0.50
CBA1 2 0.88
Table 2 (Type B)
zone weight rate
1234 1 0.52
4321 2 0.80
Finally I want the view to look like this:
name weight Table 1 rate Table 2 rate
CABA 1 0.52 0.50
AEAS 2 0.80 0.88
I tried this for my SQL statement:
SELECT 1A.name, 1B.weight, 1B.rate as A from 1A, 1B WHERE 1A.zone = 1B.zone
UNION ALL
SELECT 2A.name, 2B.weight, 2B.rate as B from 2A, 2B WHERE 2A.zone = 2B.zone
I have also tried a couple joins statements after reading unions must have matching column counts but I can't seem to hit the right query. Any ideas what I'm doing wrong or how I can achieve this with a query?
Any help is greatly appreciated!
Updated with Fiddle example here: http://sqlfiddle.com/#!5/37c19/3/0
Here is a query that will produce something similar to your example:
SELECT
ZonesOne.name
, RatesOne.weight
, RatesOne.rate as Table1Rate
, RatesTwo.Rate AS Table2Rate
FROM ZonesOne, RatesOne, RatesTwo
WHERE
RatesOne.zone = ZonesOne.zone
AND RatesOne.Weight = RatesTwo.weight
UNION ALL
SELECT
ZonesTwo.name
, RatesOne.weight
, RatesOne.rate as Table1Rate
, RatesTwo.Rate AS Table2Rate
FROM ZonesTwo, RatesOne, RatesTwo
WHERE
RatesOne.zone = ZonesTwo.zone
AND RatesOne.Weight = RatesTwo.weight
However, your Table 1 Rate and Table 2 Rate seem to be switched around. Also, your data from ZonesTwo has two entries for "DBAA".

Grouping 'like' rows in SQL and summing?

I have a report that looks like this:
Notice the rows that I have circled (row 2 and 3 in the 1109 group). These rows have the same MemberSep, Location, and Consumer text. The only difference is they each have different values for the TODKWH001 and TODKWH002 fields.
What I'd like to do is group rows like this together and sum the TODKWH001 and TODKWH002 fields together.
So, instead of these two rows:
00002574027 00000003105401 YEAGER FMS PMP 50 13 00 0 1
00002574027 00000003105401 YEAGER FMS PMP 50 13 00 4998 81
I'd have just one row:
00002574027 00000003105401 YEAGER FMS PMP 50 13 00 4998 82
Can I do this in SQL? Or should I try to do the grouping in my report?
Also, here is my SQL that I use to populate the report now:
SELECT CAR1.CAV_MBRHISTDETL.MBRSEP,
CAR1.CAV_MBRHISTDETL.LOCATION,
CAR1.CAV_MBRHISTDETL.BILLTYPE,
CAR1.CAV_MBRHISTDETL.BILLMOYR,
CAR1.CAV_MBRHISTDETL.RATE,
CAR1.CAV_LOCINFODETL.DIST,
CAR1.CAV_DEMANDHISTDETL.TODKWH_001,
CAR1.CAV_DEMANDHISTDETL.TODKWH_002,
CAR1.CAV_LOCINFODETL.ADDR1, CAR1.CAV_DEMANDHISTDETL.READTYPE
FROM CAR1.CAV_LOCINFODETL, { oj CAR1.CAV_MBRHISTDETL LEFT OUTER JOIN
CAR1.CAV_DEMANDHISTDETL ON CAR1.CAV_MBRHISTDETL.MBRSEP =
CAR1.CAV_DEMANDHISTDETL.MBRSEP AND
CAR1.CAV_MBRHISTDETL.BILLMOYR = CAR1.CAV_DEMANDHISTDETL.BILLMOYR }
WHERE CAR1.CAV_MBRHISTDETL.LOCATION = CAR1.CAV_LOCINFODETL.LOCATION AND
(CAR1.CAV_MBRHISTDETL.BILLMOYR IN ('1104', '1105', '1106', '1107', '1108','1109')) AND
(CAR1.CAV_MBRHISTDETL.RATE = '0096') AND (CAR1.CAV_MBRHISTDETL.BILLTYPE IN ('00', '01'))
ORDER BY CAR1.CAV_LOCINFODETL.DIST,
CAR1.CAV_MBRHISTDETL.BILLMOYR,
CAR1.CAV_MBRHISTDETL.MBRSEP
Work this into your current query:
SELECT MemberSep, Location, Consumer, SUM(TODKWH001), SUM(TODKWH002)
FROM yourtable
GROUP BY MemberSep, Location, Consumer

Trouble writing a query to select one row per "date", given certain conditions

I am having trouble writing a query to select one row per "date", given certain conditions. My table has this structure:
ID date expiration callput iv delta
1 1/1/2009 1/20/2009 C 0.4 0.61
2 1/1/2009 1/20/2009 C 0.3 0.51
3 1/1/2009 2/20/2009 C 0.2 0.41
I would like to write a query with the following characteristics:
For each row, calculate the "days", i.e. the expiration date minus the date. For instance, for row one, the "days" is 19 (1/20 minus 1/1)
The result set should only have rows with a "days" of between 15 and 50
The "callput" value must be "C"
For each date, show only one row. That row should have the following characteristics:
The delta should be greater than 0.5
The delta should be the smallest number greater than 0.5
If there are two rows, the row with the lower days should be selected
Here is 'days' for the sample data above:
ID date expiration days callput iv delta
1 1/1/2009 1/20/2009 19 C 0.4 0.61
2 1/1/2009 1/20/2009 19 C 0.3 0.51
3 1/1/2009 2/20/2009 50 C 0.2 0.41
For my sample dataset, the answer should be row 2, because row 2's "delta" is above 0.5, row 2's delta of 0.51 is closer to 0.5 than row 1's 0.61, and row 2's "days" of 19 is less than row 3's "days" of 50.
This is the query I've written so far:
SELECT date, Min(delta) AS MaxOfdelta, [expiration]-[date] AS days
FROM RAWDATA
WHERE (((delta)>0.5) AND ((callput)="C") AND (([expiration]-[date])>=15 And ([expiration]-[date])<=50))
GROUP BY date, [expiration]-[date]
ORDER BY date;
This works somewhat, but sometimes, there are multiple rows for one date, because two rows on a given day can have a "days" between 15 and 50. I can't get my query to obey the rule "If there are two rows, the row with the lower days should be selected". I would also like the "iv" value for that row to be present in my query result set.
I happen to be using Microsoft Access, but syntax for any SQL engine would be appreciated! :-)
What you can do is select the right rows in a subquery. This query should find the rows you're looking for:
select [date], min([expiration]-[date])
from rawdata
where delta > 0.5
and callput = 'C'
and [expiration]-[date] between 15 and 50
group by [date]
To find the delta that belongs to these rows, put it in a subquery and join on it:
select *
from rawdata
inner join (
select [date]
, min([expiration]-[date]) as days
from rawdata
where delta > 0.5
and callput = 'C'
and [expiration]-[date] between 15 and 50
group by [date]
) as filter
on filter.date = rawdata.date
and filter.days = rawdata.[expiration] - rawdata.[date]
where delta > 0.5
and callput = 'C'
To search for the lowest delta within rows with identical "days", you could add another subquery:
select
SubDaysDelta.date
, SubDaysDelta.MinDays
, SubDaysDelta.MinDelta
, min(rawdata.iv) as MinIv
from rawdata
inner join (
select
SubDays.date
, SubDays.MinDays
, min(delta) as MinDelta
from rawdata
inner join (
select [date]
, min([expiration]-[date]) as MinDays
from rawdata
where delta > 0.5
and callput = 'C'
and [expiration]-[date] between 15 and 50
group by [date]
) as SubDays
on SubDays.date = rawdata.date
and SubDays.MinDays = rawdata.[expiration] - rawdata.[date]
where delta > 0.5
and callput = 'C'
group by SubDays.date, SubDays.MinDays
) as SubDaysDelta
on SubDaysDelta.date = rawdata.date
and SubDaysDelta.MinDays = rawdata.[expiration] - rawdata.[date]
and SubDaysDelta.MinDelta = rawdata.delta
where delta > 0.5
and callput = 'C'
group by SubDaysDelta.date, SubDaysDelta.MinDays, SubDaysDelta.MinDelta
The first subquery "SubDays" searches for rows with the lowest "days". The second subquery "SubDaysDelta" searches for the lowest delta within the "SubDays" set. The outer query filters any duplicates remaining.
It would be more readable and maintainable if you'd use views. The first view could filter on callput and the 15-20 "days" limit. That'd make it a lot easier.
VBA!
I wish I could be as thorough, dedicated and helpful a servant as Andomar. I can only up-vote his answer in sheer awe of him.
However ... I would point out there are perhaps compelling reasons to switch to VBA. Even if you are new to VBA, the benefits in control and trouble shooting may put you ahead. And I'd guess any new learning will help elsewhere in your project.
I wish I would provide a complete answer as Andomar did. But give it a whack.