Have blank fields in query on purpose - sql

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.).

Related

SQL Query returning more rows when I add in a specific column from table than without

Feeling a bit confused here, I have a query running against SQL Server where I am trying to return a single row per trans_id and action number (they may be repeated). The trans column would be different in the repeated rows so I only ever want to return the highest (max value) of the trans column to give me the single row per trans_id and action number.
This seems to do the trick:
SELECT DT.trans_id,
MAX(DT.trans),
DA.ACTION_NO,
DT.[Title] AS [Case Title],
DA.[Description] AS [Action Description],
DA.ACTION_TIME_LIMIT AS [Action Deadline],
DA.Performed AS [Action Perfomed]
FROM synergi.stg_D_TRANS DT
INNER JOIN EQDW_Stg.synergi.stg_D_ACTION DA ON DA.TRANS = (SELECT MAX(TRANS)FROM synergi.stg_D_TRANS WHERE trans_id = DT.TRANS_ID)
WHERE DT.TRANS_ID != 0
GROUP BY DT.TRANS_ID,
DA.ACTION_NO,
DT.TITLE,
DA.DESCRIPTION,
DA.PERFORMED,
DA.ACTION_TIME_LIMIT;
However, when I add in this specific column, DT.TRANS_DATE, I get an additional 18 rows because it is returning one of the trans_id with 2 different trans numbers instead of just the rows with the max.
Why would adding in the date column affect this when I have numerous other columns from the same table that adding/removing don't seem to change the result at all.
SELECT DT.trans_id,
MAX(DT.trans),
DA.ACTION_NO,
DT.[Title] AS [Case Title],
DA.[Description] AS [Action Description],
DA.ACTION_TIME_LIMIT AS [Action Deadline],
DA.Performed AS [Action Perfomed],
DT.TRANS_DATE
FROM synergi.stg_D_TRANS DT
INNER JOIN EQDW_Stg.synergi.stg_D_ACTION DA ON DA.TRANS = (SELECT MAX(TRANS)FROM synergi.stg_D_TRANS WHERE trans_id = DT.TRANS_ID)
WHERE DT.TRANS_ID != 0
GROUP BY DT.TRANS_ID,
DA.ACTION_NO,
DT.TITLE,
DA.DESCRIPTION,
DA.PERFORMED,
DA.ACTION_TIME_LIMIT,
DT.TRANS_DATE;

Selecting additional data/values to display as column in query or in form

I have an employee index, that I need to run queries on for each employee, and display that output along with the original employee.
So say the employee index has an ID, batch, status, and multiple other columns. I have a table where I keep track of every time a column in the employee index changes. I want to display and later export dates of when certain columns use to equal other values, right along side the original employee row.
Naturally I tried creating a form to display multiple value, added a text box to hold my extra information, and changed the value of the text box for each Form_Current event in VBA.
Private Sub Form_Current()
Me.txtPhaseOne = SimpleLookup("SELECT TOP 1 ChangeDate FROM EmployeeVariables WHERE VariableName = ""Batch"" AND EmployeeID = " & Me.Recordset("ID Number") & " ORDER BY ChangeDate ASC", "ChangeDate")
End Sub
It seemed to work at first...
Until I realized the dates were set to whatever the current record date should be
[
So then I tried a join:
SELECT
EmployeeIndex.[ID Number],
EmployeeIndex.Batch,
EmployeeIndex.Status,
EmployeeIndex.[First name],
EmployeeIndex.[Last name],
EVA.ChangeDate as Phase1
FROM
EmployeeIndex
(SELECT TOP 1 ChangeDate FROM EmployeeVariables WHERE EmployeeID = EmployeeIndex.[ID Number] ORDER BY CHangeDate) EVA
Which would work, if I could some how prefetch EmployeeIndex.[ID Number]. (I didn't name these columns) Except I haven't got the slightest clue and I'm running on fumes.
SELECT
EmployeeIndex.[ID Number],
EmployeeIndex.Batch,
EmployeeIndex.Status,
EmployeeIndex.[First name],
EmployeeIndex.[Last name],
EVA.ChangeDate as Phase1
FROM
EmployeeIndex
INNER JOIN
(SELECT TOP 1 EmployeeID, ChangeDate FROM EmployeeVariables WHERE ORDER BY ChangeDate) EVA
ON EmployeeIndex.[ID Number] = EVA.EmployeeID
Try with a subquery:
SELECT
EmployeeIndex.[ID Number],
EmployeeIndex.Batch,
EmployeeIndex.Status,
EmployeeIndex.[First name],
EmployeeIndex.[Last name],
(SELECT Max(EVA.ChangeDate)
FROM EmployeeVariables AS EVA
WHERE EVA.EmployeeID = EmployeeIndex.[ID Number]) AS Phase1
FROM
EmployeeIndex
Cross Apply will be perfect for your TOP 1 case, as it will run for each employeeID rather than just joining on 1

MS Access: First () function in query not retrieving correct values

I have 4-6 tables, but the main ones are LRTTable and TempTable. They are joined using INNER JOIN and result set is LEFT JOIN with other 6 tables.
LRTable and Temp Table contains Store ID ,PCC Id ,COF NO, Line of Business Name, Status, Address and few other columns. LRTable can have duplicate records for same Store ID with different COF No(0,7987987) and Line of Business Name(Food and Retail).
Temp Table has Store ID, PCC. Combination of these two are primary key in Access.
Query is correctly able to pull all the data from LRTable based on TempTable. For example, query pulls all the records (including duplicates)from LRtable when there is match in Temp Table and are sorted based on status code and line of business. By doing so I am ensuring the active and food records are available on the top.
What I need to achieve is, instead of having multiple records for each store in the query result set, I want only top record from set of records of each store from the result set. I am able to this with the first function, however its picking values fron LRTable rather than sub query result
Is there a way I can just pick from sub query ?
LRTable:
Store ID PCC ID Status Line of Business Name COF No
---------- -------- ---------- -----------------------
1 123 Active Food 999
1 123 InActive Food 89899
1 123 Active Retail 0
2 222 Active Retail 0
2 222 InActive food 76767
TempTable
Store ID : 1 and 2
PCC : 123 and 222
Result:
- 1,0
- 2,0
Expected output:
- 1,999
- 2,76767
Below is the query with first
SELECT StoreID,FIRST (COF No) from
(
select * from
(
SELECT distinct TempTable.[Avendra ID],
TempTable.[Marsha Code],
LRTable.[Concept Name],
LRTable.[Outlet (Store) Name],
LRTable.[Outlet Street Address Text ID],
LRTable.[Outlet City ID],
LRTable.[Outlet State ID],
LRTable.[Outlet Zip Code ID],
LRTable.[Status Code ID],
LRTable.[Outlet SAP ID ID],
LRTable.[Outlet PCC ID ID],
LRTable.[COF Number ID],
CLPSource.[Brand Mandated],
FROM (
(
(
(
(
(
LRTable INNER JOIN TempTable
ON LRTable.[Outlet PCC ID ID] = TempTable.[PCC ID]
AND LRTable.[Store Number ID] = TempTable.[Store Number ID]
) LEFT JOIN CLPSource
ON TempTable.[Avendra ID] = CLPSource.[Avendra Customer ID]
) LEFT JOIN OasisReportSource
ON TempTable.[Marsha Code] = OasisReportSource.[Marsha])
ORDER BY LRTable.[Status Code ID],
LRTable.[Line of Business Name]))
Group by LRTable.[StoreID]
Using the data, actual output and desired output you provide in your LRTemp table and Temp Table panels, I reckon can be done using the FIRST aggregate function i.e.
SELECT StoreID, FIRST(PCC ID), FIRST(Status), FIRST(Line of Business Name), FIRST (COF No)
FROM (joined LRTable and TempTable)
GROUP BY StoreId
You should then get the data of first-occurring record for each StoreId.

LOOP and COUNT in a SELECT statement SQL SERVER

I have a view with these columns
and i want to extract this data
i am doing it in SQL Server manually in several steps and i want to automate it so that i can run a select statement directly from a macro and save it in excel file.
Here is what i am doing
select distinct
CASE
WHEN Userid ='jsolar' THEN 'Jack Solar'
WHEN Userid ='jkrcmarikova' THEN 'Jana Krcmarikova'
WHEN Userid ='lfialova' THEN 'lucia fialova'
WHEN Userid ='zsnopkova' THEN 'zuzana snopkova'
END AS [User Name]
, Region
from [SC].[vw_X86_Orders_By_UserID_GAMMA]
order by Region, [User Name]
Then i copy the result in excel file and run other queries for each user
SELECT Count ( DISTINCT [Order Number])
FROM [SC].[vw_X86_Orders_By_UserID_GAMMA]
where Userid LIKE 'jsolar'
AND Region LIKE 'CENTRAL'
and [Order Entry Date] = '2016-10-27'
i save this result in number of distinct order number. Then i run this query
SELECT Count ( DISTINCT CONCAT ([Order Number], [Line No]))
FROM [SC].[vw_X86_Orders_By_UserID_GAMMA]
where Userid LIKE 'jsolar'
AND Region LIKE 'CENTRAL'
and [Order Entry Date] = '2016-10-27'
I save this result in number of distinct order number concatenated with line no. And i repeat the same for each user
At the end it should be something like this in Excel
Is there a way how to do this in one select statement to loop each user and count for all users at the same time ? Thank you very much.
I really don't think you need a "LOOP". SQL operates best in set based operations returning recordsets with many rows. So we need to treat [SC].[vw_X86_Orders_By_UserID_GAMMA] as an entire set and simply update the case statement to translate all users names (assuming you have to have them, or you could do a vlookup on the userID in excel after the fact)
I think what you're really after is the count(distinct column) in combination with a group by on userID and region.
Based on comments I think you would need to amend the case statements in the select and group by to contain the translation for all the users.
I think there's too many unknowns to provide a 100% correct response but here's a shot across the bow..
SELECT CASE WHEN Userid ='jsolar' THEN 'Jack Solar'
WHEN Userid ='jkrcmarikova' THEN 'Jana Krcmarikova'
WHEN Userid ='******' THEN '**** *******'
WHEN Userid ='****' THEN '***** ****' END AS [User Name]
, Region
, count(distinct [Order Number]) as cntDistinctOrders
, count(Distinct concat([order Number], [Line No]) as cntDistinctOrderLines
FROM [SC].[vw_X86_Orders_By_UserID_GAMMA]
WHERE [Order Entry Date] = '2016-10-27'
-- and Region = 'CENTRAL' don't think you need this the group by handles same names in different regions keeping them distinct and counts seperate.
GROUP BY CASE WHEN Userid ='jsolar' THEN 'Jack Solar'
WHEN Userid ='jkrcmarikova' THEN 'Jana Krcmarikova'
WHEN Userid ='******' THEN '**** *******'
WHEN Userid ='****' THEN '***** ****'
END
, Region
ORDER BY [User Name], Region
To put this in plain English...
You want all the usernames and regions for each user in the [SC].[vw_X86_Orders_By_UserID_GAMMA] schema.table showing the distinct count of orders and order lines for a specific date.
If you can use a Vlookup in excel for the names you could getaway without the case statements and all that extra code..
SELECT UserId [User Name]
, Region
, count(distinct [Order Number]) as cntDistinctOrders
, count(Distinct concat([order Number], [Line No]) as cntDistinctOrderLines
FROM [SC].[vw_X86_Orders_By_UserID_GAMMA]
WHERE [Order Entry Date] = '2016-10-27'
-- and Region = 'CENTRAL' don't think you need this the group by handles same names in different regions keeping them distinct and counts seperate.
GROUP BY CASE UserID
, Region
ORDER BY [User Name], Region
--note because the group by executes before the select statement, we have to group by the USERID and not the alias name of [User name]

Asking to enter a parameter value that already exists

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]