MS Access table: Populating one column based on others with SWITCH - ms-access-2013

I am working with two(2) tables in my MS Access database. ChangeCodes and Items. ChangeCodes has the following columns (Prefix, Suffix, and Description).
Here are sample rows:
Prefix Suffix Description
BE 2178 Business Enhancement
DP 1033 Development Problem
PE 2137 Performance Enhancement
PP 1687 Production Problem
TC 1001 Temporary Change
TE 1003 Technical Enhancement
WA 1005 Work Around
Items has the following columns (Item_ID, Item_Name, Entered_Date, Defect_Num, Mod_Num, User_Entered, Code_Flag, Complete_Flag)
Here are sample rows:
BE0999 Fee Increment Stuffer 8/9/2001 NW5639 nakiris Yes Yes
PE1084 Regionalizing RA99Z5UT 5/27/2008 NW7128 LUCKMAB Yes No
PE1480 Resiliency task 300 2/22/2013 NW7768 SOUNDM2 No No
PP1092 Bad check 11/3/2003 NW6828 sangis1 No No
PP1093 To Avoid contention 11/7/2003 NW6829 narass1 No No
PP1094 Change to ensure ZERO 11/12/2003 NW6830 paletic No No
As you can see Item_ID is a combination of the ChangeCodes.Preix and a 4 digit Suffix. I wish to populate the Suffix of each row of the ChangeCodes table with the MAX value of the corresponding Item_ID in the Items table. Is this SQL that will do this? I have tried the following but the syntax is not correct.
SELECT
IIf([ChangeCodes.Prefix] = "BE", SELECT Mid(Max(Items.Item_ID),3,4) AS Expr1
FROM Items
WHERE Items.Item_ID LIKE 'BE*';),
IIf([ChangeCodes.Prefix] = "DP", SELECT Mid(Max(Items.Item_ID),3,4) AS Expr1
FROM Items
WHERE Items.Item_ID LIKE 'DP*';),
IIf([ChangeCodes.Prefix] = "PE", SELECT Mid(Max(Items.Item_ID),3,4) AS Expr1
FROM Items
WHERE Items.Item_ID LIKE 'PE*';),
IIf([ChangeCodes.Prefix] = "PP", SELECT Mid(Max(Items.Item_ID),3,4) AS Expr1
FROM Items
WHERE Items.Item_ID LIKE 'PP*';),
IIf([ChangeCodes.Prefix] = "TC", SELECT Mid(Max(Items.Item_ID),3,4) AS Expr1
FROM Items
WHERE Items.Item_ID LIKE 'TC*';),
IIf([ChangeCodes.Prefix] = "TE", SELECT Mid(Max(Items.Item_ID),3,4) AS Expr1
FROM Items
WHERE Items.Item_ID LIKE 'TE*';),
IIf([ChangeCodes.Prefix] = "WA", SELECT Mid(Max(Items.Item_ID),3,4) AS Expr1
FROM Items
WHERE Items.Item_ID LIKE 'WA*';)
As Suffix
FROM ChangeCodes;
Any idea how to accomplish it ?

If I understood this well you have your table Items Id with some values and you want to populate changecodes based on your items table.
On your query you have Mid(Max(Items.Item_ID),3,4). Max only works with numbers and Item_ID is a string "BE0999".
You can try changing that expression for Max(CDec(Mid(Items.Item_ID),3,4)) or Max(CInt(Mid(Items.Item_ID),3,4)).
The mid function will get you this part of the code "0999", however it is still a string you need to convert it to number before you use Max using CInt or CDec.
Not sure if it will work but worth the try.

Related

Subtract Group Columns in Matrix table in SSRS

I have a matrix table grouped by WBS and Acc at row level.
Then i have a column group "Table" with Plan & Actuals. What i want to get is (Plan)-(Actuals)
I used the below expression in a outer group column
=SUM(IIF(Fields!Table.Value = "Plan", (Fields!Total.Value),0))-SUM(IIF(Fields!Table.Value = "Actuals", (Fields!Total.Value),0))
The total.value is the total of plan and actuals individually.
I am getting an error in the calculated column (Expression)
The Matrix TableThe Matrix Table
The Dataset and Outputs (expected and current)
If you only ever have two fixed values in your Table column then you should be able to do this without the need for custom code.
Something like this...
=SUM(IIF(Fields!Table.Value = "Plan", Fields!Total.Value,0))
-
SUM(IIF(Fields!Table.Value = "Actuals", Fields!Total.Value,0))
UPDATE for clarity
I have created a report based on your sample data and the above works as expected.
I created a dataset using the following query
DECLARE #t TABLE (WBSNumbers varchar(20), [Table] varchar(20), Account Varchar(20), Total float)
INSERT INTO #t
SELECT 'xxx', 'Plan', 'Capital', 96875 UNION ALL
SELECT 'xxx', 'Plan', 'Expense', 40625 UNION ALL
SELECT 'xxx', 'Actuals', 'Capital', 229949 UNION ALL
SELECT 'xxx', 'Actuals', 'Expense', 2848 UNION ALL
SELECT 'yyy', 'Actuals', 'Expense', 0
SELECT * FROM #t
I added a matrix with row groups for WBSNumbers and Account and a column group for Table.
I added a new column outside the column group with the expression stated above
I added an extra row for the headers but this was not really required - just to make it match the expected output more closely.
The final design looks like this.
The final output looked like this...
Other than the fact that the sample data does not match the supplied expected output for ABS yyyy this works as expected.

I tested in my SQL Developer one case about "Subquery in Order By"

I have question about "Subquery in Order by clause". The below request returns the error. Is it means that Subquery in Order by clause must be scalar?
select *
from employees
order by (select * from employees where first_name ='Steven' and last_name='King');
Error:
ORA-00913: too many values
00913. 00000 - "too many values"
Yes, it means that if you use a subquery in ORDER BY it must be scalar.
With select * your subquery returns multiple columns and the DBMS would not know which of these to use for the sorting. And if you selected one column only, you would still have to make sure you only select one row of course. (The difference is that Oracle sees the too-many-columns problem immediately, but detect too many rows only when fetching the data.)
This would be allowed:
select * from employees
order by (select birthdate from employees where employee_id = 12345);
This is a scalar query, because it returns only one value (one column, one row). But of course this still makes as little sense as your original query, because the subquery result is independent from the main query, i.e. it returns the same value for every row in the table and thus no sorting takes effect.
A last remark: A subquery in ORDER BY makes very seldomly sense, because that would mean you order by something you don't display. The exception is when looking up a sortkey. E.g.:
select *
from products p
where type = 'shirt' and color = 'blue' and size in ('S', 'M', 'L', 'XL')
order by (select sortkey from sizes s where s.size = p.size);
It means that valid options for ORDER BY clause can be
expression,
position or
column alias
A subquery is neither of these.

Trying to display top 3 amount from a table using sql query in oracle 11g..column is of varchar type

Am trying to list top 3 records from atable based on some amount stored in a column FTE_TMUSD which is of varchar datatype
below is the query i tried
SELECT *FROM
(
SELECT * FROM FSE_TM_ENTRY
ORDER BY FTE_TMUSD desc
)
WHERE rownum <= 3
ORDER BY FTE_TMUSD DESC ;
o/p i got
972,9680,963 -->FTE_TMUSD values which are not displayed in desc
I am expecting an o/p which will display the top 3 records of values
That should work; inline view is ordered by FTE_TMUSD in descending order, and you're selecting values from it.
What looks suspicious are values you specified as the result. It appears that FTE_TMUSD's datatype is VARCHAR2 (ah, yes - it is, you said so). It means that values are sorted as strings, not numbers - and it seems that you expect numbers. So, apply TO_NUMBER to that column. Note that it'll fail if column contains anything but numbers (for example, if there's a value 972C).
Also, an alternative to your query might be use of analytic functions, such as row_number:
with temp as
(select f.*,
row_number() over (order by to_number(f.fte_tmusd) desc) rn
from fse_tm_entry f
)
select *
from temp
where rn <= 3;

Seeking hints to simplify complex query

Building a web page to show summary data and a chart. The query to obtain my summary data appears to be overly complex and there must be a simpler way to accomplish. I'm mainly experienced with SQL Server, and under SQL Server, getting row and column level totals is done within the main query. No unions or sub queries required, unless you are doing some more complex things.
However, under Oracle 10g, this appears to be the way to accomplish the same thing.
The resulting data is put into a JSON array and populates a v1.10 DataTable.
The source data has a row containing the date, item and a count of items.
The ending table uses a pivot, becoming 8 columns, 6 for the items, a date and row-level total. I trimmed 2 columns to simplify reduce the clutter in the question. The final row has column-level totals and the final grand total. Any suggestions welcome.
The query is here
SELECT *
FROM (
SELECT TO_CHAR("DATE", 'MM/DD/YYYY') AS "DATE"
, ITEM_NAME
, SUM(ITEM_COUNT) AS TOTAL
FROM MY_VIEW
WHERE 1=1
AND "DATE" > ADD_MONTHS(TO_DATE(SYSDATE, 'DD-MM-RR'), -1)
AND ITEM_NAME IN ('ITEM-01','ITEM-02','ITEM-03','ITEM-04')
GROUP BY "DATE", ITEM_NAME
UNION ALL
SELECT TO_CHAR("DATE", 'MM/DD/YYYY') AS "DATE"
, 'ROW_TOTAL' AS ITEM_NAME
, SUM(ITEM_COUNT) AS TOTAL
FROM MY_VIEW
WHERE 1=1
AND "DATE" > ADD_MONTHS(TO_DATE(SYSDATE, 'DD-MM-RR'), -1)
AND ITEM_NAME IN ('ITEM-01','ITEM-02','ITEM-03','ITEM-04')
GROUP BY "DATE"
)
PIVOT
(
MAX(TOTAL) FOR ITEM_NAME IN ('ITEM-01','ITEM-02','ITEM-03','ITEM-04','ROW_TOTAL')
)
UNION ALL
SELECT *
FROM (
SELECT 'GRAND TOTAL' AS "DATE"
, ITEM_NAME
, SUM(ITEM_COUNT) AS TOTAL
FROM MY_VIEW
WHERE 1=1
AND "DATE" > ADD_MONTHS(TO_DATE(SYSDATE, 'DD-MM-RR'), -1)
AND ITEM_NAME IN ('ITEM-01','ITEM-02','ITEM-03','ITEM-04')
GROUP BY ITEM_NAME
UNION ALL
SELECT 'GRAND TOTAL' AS "DATE"
, 'ROW_TOTAL' AS ITEM_NAME
, SUM(ITEM_COUNT) AS TOTAL
FROM MY_VIEW
WHERE 1=1
AND "DATE" > ADD_MONTHS(TO_DATE(SYSDATE, 'DD-MM-RR'), -1)
AND ITEM_NAME IN ('ITEM-01','ITEM-02','ITEM-03','ITEM-04')
)
PIVOT
(
MAX(TOTAL) FOR ITEM_NAME IN ('ITEM-01','ITEM-02','ITEM-03','ITEM-04', 'ROW_TOTAL')
)
ORDER BY 1
And the end results should look like this:
DATE ITEM-01 ITEM-02 ITEM-03 ITEM-04 ROW_TOTAL
======================================================
4/18/17 1,063,008 460,436 106,715 97,532 1,829,364
4/19/17 1,061,819 479,338 103,946 108,179 1,859,825
4/20/17 1,095,853 536,835 107,437 101,949 1,944,677
4/21/17 1,153,345 642,364 108,940 106,988 2,121,068
4/22/17 1,075,849 633,873 102,459 99,999 2,012,710
4/23/17 913,952 591,783 95,291 100,144 1,794,358
4/24/17 1,036,377 626,043 115,105 98,339 1,977,043
4/25/17 1,079,163 602,237 118,189 100,478 2,001,529
4/26/17 1,110,499 639,640 109,793 103,360 2,069,311
4/27/17 1,119,696 620,081 105,781 108,276 2,061,452
4/28/17 1,125,676 618,763 113,234 96,326 2,057,169
4/29/17 1,026,974 620,059 102,856 96,150 1,940,394
4/30/17 903,913 539,694 83,531 97,073 1,716,114
5/1/17 1,043,598 590,027 100,272 96,519 1,932,843
5/2/17 1,074,912 623,392 101,793 97,724 2,000,981
5/3/17 1,078,865 620,662 101,699 102,900 2,010,014
5/4/17 1,090,501 628,785 110,248 103,593 2,040,658
5/5/17 1,125,984 686,945 128,657 105,356 2,150,037
5/6/17 1,031,267 625,189 117,290 99,358 1,967,819
5/7/17 921,467 551,497 97,482 93,520 1,752,940
5/8/17 1,064,291 624,366 93,463 98,860 1,979,863
5/9/17 1,085,062 661,509 97,791 98,083 2,039,114
5/10/17 1,103,794 634,868 94,364 102,345 2,033,911
5/11/17 1,107,449 617,931 94,420 103,717 2,024,126
5/12/17 1,130,463 647,744 97,616 102,684 2,079,009
5/13/17 1,056,653 621,182 96,743 99,801 1,974,710
5/14/17 970,969 583,865 87,953 97,682 1,831,516
5/15/17 1,075,979 633,102 95,356 101,336 2,003,830
5/16/17 1,094,805 634,421 96,802 99,533 2,026,891
GRAND TOTAL 30,822,183 17,596,631 2,985,226 2,917,804 57,233,276
It might go faster if you use 'analytical queries' to perform totalling without needing to run separate grouping queries. An example analytic expression might be:
Select
Sum(item_count) over(partition by date) --btw "date" is a poor name choice for a column
From
Table
Where
Item_name in ...
Or alternatively, use 'grouping sets', 'cube' or 'rollup'
The difference? Analytics establish grouping characteristics that add an extra column to a report with aggregation of the row. Grouping sets, cubes and roll ups add extra rows to a report with aggregations of a column
Apologies for not giving an example of this; they're quite an extensive topic requiring in depth discussion so it's partly beyond the scope of my answer, and partly that I'm writing this on an iPad with no recent use of them to call on from memory (the topic is that vast) and no way to test or run one, so I'll leave it as a pointer for you to do further background research. Essentially a grouping set is an instruction akin to "here's a single data set, iterate it once and perform these N number of different group by aggregates as you go.." essentially one group would be by date and name (so single lines are output) and the other group by is probably by name (so totals for each name are output)..
then do your pivot. For more info, the 'phrases in quotes' are what you'd look up in the manual/web
All this is a little bit dirty, by the way.. your reporting tool from end should really be building this summary, rather than oracle, though doing grouping (but not pivoting) in the DB helpfully reduces network traffic

Selecting data from one table or another in multiple queries PL/SQL

The easiest way to ask my question is with a Hypothetical Scenario.
Lets say we have 3 tables. Singapore_Prices, Produce_val, and Bosses_unreasonable_demands.
So Prices is a pretty simple table. Item column containing a name, and a Price column containing a number.
Produce_Val is also simple 2 column table. Type column containing what type the produce is (Fruit or veggie) and then Name column (Tomato, pineapple, etc.)
The Bosses_unreasonable_demands only contains one column, Fruit, which CAN contain the names of some fruits.
OK? Ok.
SO, My boss wants me to write a query that returns the prices for every fruit in his unreasonable demands table. Simple enough. BUT, if he doesn't have any entries in his table, he just wants me to output the prices of ALL fruits that exist in produce_val.
Now, assuming I don't know where the DBA who designed this silly hypothetical system lives (and therefore can't get him to fix this), our query would look like this:
if <Logic to determine if Bosses demands are empty>
Then
select Item, Price
from Singapore_Prices
where Item in (select Fruit from Bosses_Unreasonable_demands)
Else
select Item, Price
from Singapore_Prices
where Item in (select Name from Produce_val where type = 'Fruit')
end if;
(Well, we'd select those into a variable, and then output the variable, probably with bulk-collect shenanigans, but that's not important)
Which works. It is entirely functional, and won't be slow, even if we extend it out to 2000 other stores other than Singapore. (Well, no slower than anything else that touches 2000 some tables) BUT, I'm still doing two different select statements that are practically identical. My Comp Sci teacher rolls in their grave every time my fingers hit ctrl-V. I can cut this code in half and only do one select statement. I KNOW I can.
I just have no earthly idea how. I can't use cursors as an in statement, I can't use nested tables or varrays, I can't use cleverly crafted strings, I... I just... I don't know. I don't know how to do this. Is there a way? Does it exist?
Or do I have to copy/paste forever?
Your best bet would be dynamic SQL, because you can't parameterize table or column names.
You will have a SQL query template, have a logic to determine tables and columns that you want to query, then blend them together and execute.
Another aproach, (still a lot of ctrl-v like code) is to use set construction UNION ALL:
select 1st query where boss_condition
union all
select 2nd query where not boss_condition
Try this:
SELECT *
FROM (SELECT s.*, 'BOSS' AS FRUIT_SOURCE
FROM BOSSES_UNREASONABLE_DEMANDS b
INNER JOIN SINGAPORE_FRUIT_LIST s
ON s.ITEM = b.FRUIT
CROSS JOIN (SELECT COUNT(*) AS BOSS_COUNT
FROM BOSSES_UNREASONABLE_DEMANDS)) x
UNION ALL
(SELECT s.*, 'NORMAL' AS FRUIT_SOURCE
FROM PRODUCE_VAL p
INNER JOIN SINGAPORE_FRUIT_LIST s
ON (s.ITEM = p.NAME AND
s.TYPE = 'Fruit')
CROSS JOIN (SELECT COUNT(*) AS BOSS_COUNT
FROM BOSSES_UNREASONABLE_DEMANDS)) n
WHERE (BOSS_COUNT > 0 AND FRUIT_SOURCE = 'BOSS') OR
(BOSS_COUNT = 0 AND FRUIT_SOURCE = 'NORMAL')
Share and enjoy.
I think you can use nested tables. Assume you have a schema-level nested table type FRUIT_NAME_LIST (defined using CREATE TYPE).
SELECT fruit
BULK COLLECT INTO my_fruit_name_list
FROM bosses_unreasonable_demands
;
IF my_fruit_name_list.count = 0 THEN
SELECT name
BULK COLLECT INTO my_fruit_name_list
FROM produce_val
WHERE type='Fruit'
;
END IF;
SELECT item, price
FROM singapore_prices
WHERE item MEMBER OF my_fruit_name_list
;
(or, WHERE item IN (SELECT column_value FROM TABLE(CAST(my_fruit_name_list AS fruit_name_list)) if you like that better)

Resources