I have a three column table of price breaks called "FPB" that looks like this:
[Part Number] [Quantity] [Price]
AAA-AAAA 100 1.23
AAA-AAAA 200 1.15
BBB-BBBB 100 5.60
CCC-CCCC 500 3.21
....
Where each part number has multiple entries in multiple rows.
I'm trying to reorganize the table to look more like this
[Part Number] [Quantity1] [Price 1] [Quantity 2] [Price 2] [Quantity 3....
AAA-AAAA 100 1.23 200 1.15 ....
BBB-BBBB 100 5.60 ...
CCC-CCCC 500 3.21 ...
...
Where each part number has all its entries combined into one row. The first quantity column should have the lowest available quantity, the second should have the second smallest etc. I am trying to do this by first creating a 1-column table with just the unique part numbers using GROUP BY, and then creating more tables for each column that has the information I want in that column, and then joining it by Part Number. The problem comes when calculating the second smallest quantity for each type, done in the second to last section.
SELECT PNs.[Part Number], Q1T.Q1, P1T.Price, Q2T.Q2
FROM
(SELECT
[Part Number]
FROM FPB
GROUP BY [Part Number]
) AS PNs,
(SELECT
[Part Number],
MIN(Quantity) AS Q1
FROM FPB
GROUP BY [Part Number]
) AS Q1T,
(SELECT
*
FROM FPB
) AS P1T,
(SELECT
[Part Number],
MIN(IIF(Quantity>Q1T.Q1,Quantity)) AS Q2
FROM FPB
GROUP BY [Part Number]
) AS Q2T
WHERE
PNs.[Part Number] = Q1T.[Part Number]
AND P1T.[Part Number] = PNs.[Part Number]
AND P1T.Quantity = Q1T.Q1
AND Q2T.[Part Number] = PNs.[Part Number]
When I run this query, it asks me to enter a parameter value for Q1T.Q1, even though it already exists. If I remove the code section for Q2T, as well as any references to Q2, it will work without a problem, and it won't ask about a value for the other instances of Q1T.Q1. Why doesn't Q1T.Q1 have a value just for that section, and how can I fix it? As a side note, I'm using the SQL features of a program called PHPRunner, and its client doesn't support UPDATE/DELETE/INSERT/CREATE queries, UNION, and DISTINCT.
You're looking for something like this.
select
p1.PartNumber,
ifnull(max(p2.Quantity), 0) + 1 as LowerQuantity,
p1.Quantity as UpperQuantity,
p1.Price,
count(p2.PartNumber) + 1 as PriceTier
from
FPB p1 left outer join FPB p2
on p2.PartNumber = p1.PartNumber and p2.Quantity < p1.Quantity
From there it's easy to pivot in order to insert into a new table:
into into NewFPB (PartNumber, Quantity1, Price1, Quantity2, Price2, ...)
select
PartNumber,
min(switch(PriceTier = 1, UpperQuantity)) as Quantity1,
min(switch(PriceTier = 2, UpperQuantity)) as Quantity2, ...
min(switch(PriceTier = 1, Price)) as Price1,
min(switch(PriceTier = 2, Price)) as Price2, ...
from (
select
p1.PartNumber,
ifnull(max(p2.Quantity), 0) + 1 as LowerQuantity,
p1.Quantity as UpperQuantity,
p1.Price,
count(p2.PartNumber) + 1 as PriceTier
from
FPB p1 left outer join FPB p2
on p2.PartNumber = p1.PartNumber and p2.Quantity < p1.Quantity
) data
You might have to tweak it a little bit for Access to accept it. But the core ideas are there.
The query you call is incorrect.
Q1T is inner select statement , and in Q2T (other inner select statement) , you can't use any field from Q1T
The SQL Server raise error: Incorrect syntax near ')'.
To overcome this limitation , you should use Common Table Expressions CTE
for PNs, Q1T, P1T, Q2T
CTE is like dynamic view.
It's a new feature since sql 2008 and It's a very powerful.
review: https://technet.microsoft.com/en-us/library/ms190766(v=sql.105).aspx
Try to Draw a relational data model for these four CTE to be sure that a relation exist between them based on your where conditions.
I think the logic in this query may raise runtime error during execution:
e.g. The multi-part identifier "Q1T.Q1" could not be bound.
Edit:
For Ms-Access you can create four queries for: PNs, Q1T, P1T, Q2T every one is in a separate query, and the fifth query join these queries and add where conditions.
In this case you will not get any syntax error. and will get Data model with relation free :) .
As per your question, by the time you define the derived table Q2T, Q1T is still an invalid object. You need to try to work around this issue.
EDIT:
Suppose you only got 2-level columns to handle, the code is listed below, i tested it. It works well.
select q5.*,q3.quantity, q3.price
from
(select *
from FPB as Q1
where 0 = (select count(distinct(quantity)) from FPB as Q2 where Q2.quantity < Q1.quantity AND Q2.[part number] = Q1.[part number])) as Q5
,
(
select distinct(temp.[part number]),Q2.quantity, Q2.price from FPB as temp
left join
(select *
from FPB as Q4
where 1 = (select count(distinct(quantity)) from #test as Q2 where Q2.quantity < Q4.quantity AND Q2.[PART NUMBER] = Q4.[PART NUMBER])) as Q2
on temp.[PART NUMBER] = Q2.[PART NUMBER]
) as Q3
where Q5.[PART NUMBER] = Q3.[PART NUMBER]
Related
I am trying to run a query to gather the total items on hand in our database. However it seems i'm getting incorrect data. I am selecting selecting just the amount field and summing it using joins from separate tables based on certain parameters, however if I display additional fields such as order number, and date all of a sudden im getting different data, even though those fields are being used as filters in the query. Is it because its not in the select statement? If it needs to be in the select statement is it possible to not display them?
Here are the two queries.
-- Items On Hand
select CONVERT(decimal(25, 2), SUM(tw.amount)) as 'Amt'
from [Sales Header] sh
join
(
select *
from TWAllOrders
where [Status] like 'Released'
) tw
on tw.[Order Nb] = sh.No_
join
(
select *
from OnHand
) oh
on tw.No_ = oh.[Item No_]
where sh.[Requested Delivery Date] < getdate()
HAVING SUM(tw.Quantity) <= SUM(oh.Qty)
providing a sum of 21667457.20
and with the added columns
-- Items On Hand
select CONVERT(decimal(25, 2), SUM(tw.amount)) as 'Amt', [Requested Delivery Date], sh.No_, tw.[Status]
from [Sales Header] sh
join
(
select *
from TWAllOrders
where [Status] like 'Released'
) tw
on tw.[Order Nb] = sh.No_
join
(
select *
from OnHand
) oh
on tw.No_ = oh.[Item No_]
where sh.[Requested Delivery Date] < getdate()
group by sh.[Requested Delivery Date], sh.No_, tw.[Status]
HAVING SUM(tw.Quantity) <= SUM(oh.Qty)
order by sh.[Requested Delivery Date] ASC
Providing a sum of 12319998
I'm self taught in SQL so I may be misunderstanding something obvious, thanks for the help.
With no sample data, I am going to have to demonstrate this in principle. In the latter query you have a GROUP BY meaning the scope of the values in the HAVING will differ, and thus the filtering from said HAVING will be different.
Let's take the following sample data:
CREATE TABLE dbo.MyTable (Grp char(1),
Quantity int,
Required int);
INSERT INTO dbo.MyTable (Grp, Quantity, [Required])
VALUES('a',2,7),
('a',14,2),
('b',4, 7),
('b',3,4),
('c',17,5);
Now we'll perform an overly simplified version of your query:
SELECT SUM(Quantity)
FROM dbo.MyTable
HAVING SUM(Quantity) > SUM(Required);
This brings back the value 40; which is the SUM of all the values in Quantity. A value is returned because the total SUM of Required is 25.
Now let's add a GROUP BY like your second query:
SELECT SUM(Quantity)
FROM dbo.MyTable
GROUP BY Grp
HAVING SUM(Quantity) > SUM(Required);
Now we have 2 rows, with the values 16 and 17 giving a total value of 33. That's because the rows where Grp have a value of 'B' are filtered out, as the SUM of Quantity is lower that Required for 'B'.
The same is happening in your data; in the grouped data you have groups where the HAVING condition isn't met, so those rows aren't returned.
I am trying to add a Sum 'field' to a SELECT query, where it sums up the data on that row, and returns it as a field. The problem seems to lie with the GROUP BY statement, that I seemingly have to use. When using this, it groups the 'sums' together, rather than provides a total for each row of data.
SELECT PS_DB.TeamName AS [Team Name], TM_adjData.SM_adjName AS Adjudicator, PS_DB.WeekEnding AS [Week Ending], PS_DB.Pts AS [BAU Points], PS_DB.Adhc, Sum(PS_DB.Pts + PS_DB.Adhc) as [Total], PS_DB.Approved AS Approved
FROM PS_DB
LEFT JOIN TM_adjData on PS_DB.Adjudicator = TM_adjData.SM_empNum
GROUP BY TeamName, SM_adjName, WeekEnding, Pts, Adhc, Approved
This returns 518 rows, where as if I remove the GROUP BY section and the 'sum' field, it returns 1,608 rows (which is correct).
How can I get the 1,608 rows with the sum next to it?
I think you can do what you want with a correlated subquery:
SELECT p.TeamName AS [Team Name], a.SM_adjName AS Adjudicator,
p.WeekEnding AS [Week Ending], p.Pts AS [BAU Points], p.Adhc,
(SELECT SUM(p2.Pts + p2.Adhc)
FROM PS_DB as p2
WHERE p2.TeamName = p.TeamName -- perhaps more conditions are needed
) as [Total],
p.Approved AS Approved
FROM PS_DB as p LEFT JOIN
TM_adjData as a
ON p.Adjudicator = a.SM_empNum;
If you want to perform a sum for each row, you don't group rows, instead you simply perform addition. I've added Nz() function to properly address issue where any of the value being added is null and treat it as 0:
SELECT
PS_DB.TeamName AS [Team Name],
TM_adjData.SM_adjName AS Adjudicator,
PS_DB.WeekEnding AS [Week Ending],
PS_DB.Pts AS [BAU Points],
PS_DB.Adhc,
Nz(PS_DB.Pts,0) + Nz(PS_DB.Adhc,0) as [Total], -- this is your row sum
PS_DB.Approved AS Approved
FROM PS_DB
LEFT JOIN TM_adjData on PS_DB.Adjudicator = TM_adjData.SM_empNum
SUM is an aggregate function and it works on entire table or groups of data (with GROUP BY).
I am using the count to get the total number, but missing team 4 value.
Here is my query:
select
[Team Number], Count([Followup]) as [FeedbackNeedToBeReview]
from
[TICKETFEEDBACK].[db_fdd63213_8581_4bef_ae7d_004c96c5873a].[Access].[TicketFeedback Tool]
where
[Followup] = ''
group by
[Team Number]
order by
CAST([Team Number] as int)
Output :
If I set the condition for Team 4,Nothing is returned.
select
[Team Number], Count([Followup]) as [FeedbackNeedToBeReview]
from
[TICKETFEEDBACK].[db_fdd63213_8581_4bef_ae7d_004c96c5873a].[Access].[TicketFeedback Tool]
where
[Followup] = ''
and [Team Number] = '4'
group by
[Team Number]
order by
CAST([Team Number] as int)
Output :
how can i return
TeamNumber [FeedbackNeedToBeReview]
4 0
The correct way of this query is in my opinion as follows:
We need a table named Teams with TeamNumber field. In this table all possible teams should be existing including Team#4
Then In your query join Teams table with Feedback table. You will be using LEFT JOIN to feedbacks table
This will ensure that all rows from Teams including Team number 4 will be in the output with no feedbacks so 0 will be counted
An other option is simply using a SQL CTE expression to simulate this as follows
declare #team int = 4
;with teams_cte as (
select #team teamnumber
)
select
teams_cte.teamnumber, COUNT(Followup) snt
from teams_cte
left join feedbacks on feedbacks.teamnumber = teams_cte.teamnumber
where teams_cte.teamnumber = 4
group by teams_cte.teamnumber
which returns 4-0 as desired
I have to process in an sql as follows. each order is made up of many detail rows. I only need to look at one table, TRA99.
Order number TRAN CODE
123 QEE
123 #23
123 ABC
SELECT
ALL OTRIDC, OTCOM#, OTORD#, OTFL50, OTTRND, OTTRT, OTENT#,
OTSFX#,
OTREL#, OTUSRN, OTTRNC, OTTRN$, OTFL01
FROM ASTDTA.OETRANOT T01
WHERE OTTRNC IN ('QEE', 'QNE')
I want all the Order # which have 'QEE' or 'QNE'. These are QUote codes. We want a report that will tell us, which quotes orders' converted to a real order and which did not.
then if they have as well #23, this tells me that the order was converted or became an actual order. I am not sure how to do this in 1 sql query i was thinking to create a view for all QEE and QNE codes. then run a second query against that looking for #23.
Based on what I think you're trying to do. This will give you all the order numbers which are associated with both QEE or QNE and #23
SELECT T1.OrderNumber
FROM TRA99 T1
WHERE T1.OrderNumber in (
SELECT OrderNumber
FROM TRA99
WHERE TCode IN ('QEE','QNE')
)
AND T1.TCode='#23'
GROUP BY T1.OrderNumber
What you need to do is use a GROUP BY also a EXISTS sub-query can check for the existence of your flag
select t1.[Order Number]
,(CASE WHEN EXISTS(select *
from TRA99 as t2
where t1.[Order Number] = t2.[Order Number]
and t2.[Tran Code] = '#23')
THEN
cast(1 as bit)
ELSE
cast(0 as bit)
END) as HasFlag
from TRA99 as t1
where t1.[Tran Code] in ('QFE', 'QNE')
group by t1.[Order Number]
Working example
NOTE: you did not list what DBMS you are using so I wrote this against Microsoft Sql Server syntax, but you can translate this concept to any DBMS you need.
I have a Microsoft Access database with the following table:
TableName: Parts
Cabinet ID - Number
Part ID - Number
PartClass - Number
Width - Number (double)
Length - Number (double)
Description - Text
I use the following SQL queries to get three views:
DoorPanels:
SELECT Parts.[Cabinet ID], Count(Parts.[Cabinet ID]) as Qty, Parts.Description as Name Parts.Width, Parts.Length
FROM Parts
WHERE PartClass=13 GROUP BY Parts.[Cabinet ID], Parts.Description, Parts.Width, Parts.Length
ORDER BY Parts.[Cabinet ID]
DoorRails:
SELECT Parts.[Cabinet ID], Count(Parts.[Cabinet ID]) as Qty, Parts.Description as Name Parts.Width, Parts.Length
FROM Parts
WHERE PartClass=14 GROUP BY Parts.[Cabinet ID], Parts.Description, Parts.Width, Parts.Length
ORDER BY Parts.[Cabinet ID]
DoorStiles:
SELECT Parts.[Cabinet ID], Count(Parts.[Cabinet ID]) as Qty, Parts.Description as Name Parts.Width, Parts.Length
FROM Parts
WHERE PartClass=15 GROUP BY Parts.[Cabinet ID], Parts.Description, Parts.Width, Parts.Length
ORDER BY Parts.[Cabinet ID]
This gives me three separate views that hold only the parts that I want. Now, I have an additional table:
TableName: Doors
Cabinet ID - Number
Door ID - Number
Width - Number (double)
Height - Number (double)
I use another query to create a view for this as well:
SELECT Doors.[Cabinet ID], Count(Doors.[Door ID]) as Qty FROM Doors GROUP BY Doors.[Cabinet ID] ORDER BY Doors.[Cabinet ID]
So, finally I want to join all of these together...but this is where I am at a bit of a loss. What I need to get, would be a result like this, from the data that I have retrieved:
Doors.[Cabinet ID] Doors.Qty DoorRails.* DoorStiles.* DoorPanels*
1 2 [data] [data] [data]
[data] [data] [data]
2 3 [data] [data] [data]
[data] [data] [data]
[data] [data] [data]
Now, to decipher what I am talking about in the result above, you can see that for [Cabinet ID] 1, I have a door quantity of 2, so I need 2 records for the parts for that door. [Cabinet ID] 2 has 3 doors, so I need 3 records for all the door parts for that.
Now, I understand that this is a little complicated, and possibly...well...impossible, but I have been working on this for a few days and haven't come up with anything. If it's not possible, I would appreciate the experts here telling me so.
Just for FYI - I can't make ANY changes to the database or the structure, all that I can do is run Queries against it.
I think the easiest way to do this would be via a Cartesian join to a table that simply has rows with a single field incrementing from 1 to the maximum number of doors you will ever need. For example,
Counter.Num
-----------
1
2
3
If you cannot even create local tables for your own use, you could simulate the above using a UNION query. Access has some limitations here, so you'll need to reference an existing table even though you won't use any of its rows. For example,
SELECT TOP 1 1 AS Num FROM msysobjects
UNION ALL
SELECT TOP 1 2 AS Num FROM msysobjects
UNION ALL
SELECT TOP 1 3 AS Num FROM msysobjects
Then use this Counter table/query as follows:
SELECT D.[Cabinet ID], C.Num & " of " & Count(D.[Door ID]) AS [Door Number],
DR.*, DS.*, DP.*
FROM Counter AS C,
((Doors AS D INNER JOIN DoorRails AS DR ON D.CabinetID=DR.CabinetID)
INNER JOIN DoorStiles AS DS ON D.CabinetID=DS.CabinetID)
INNER JOIN DoorPanels AS DP ON D.CabinetID=DP.CabinetID
WHERE C.Num <= Count(D.[Door ID])
GROUP BY D.[Cabinet ID]
ORDER BY D.[Cabinet ID], C.Num
This will be a slightly different result than your sample. The difference is the Cabinet ID will be repeated for each door. Since the quantity would also be repeated, I substituted the door number instead (in the form of "1 of 2", "2 of 2", etc.).