mapping of areas with multiple users - sql

I have areas like sector 1, sector 1 a, sector 1 b, sector 1 c and multiple cable operators who are working in either full sector(i.e sector 1) or any of the sub sectors. I have created table of cable operators and want to map them with areas. If I set up area table like sector 1, sector 1 a, sector 1 b, sector 1 c each with their own Primary Key then how can I reference these sectors in single row of cable operators provided that we have to get the cable operators working in that particular sector.
My table structures are as follows:
Operators
| id | name
| 1 | 'abc'
| 2 | 'def'
| 3 | 'ghi'
areas
| id | name
| 1 | 'sector 1'
| 2 | 'sector 1a'
| 3 | 'sector 1b'
| 4 | 'sector 1c'
| 5 | 'sector 1d'
| 6 | 'sector 2'
| 7 | 'sector 2a'
| 8 | 'sector 2b'
| 9 | 'sector 2c'
| 10 | 'sector 2d'
I have operatorsareas table where I have map operators with areas as follows:
operatorsareas
| op_id | area_id
| 1 | 1
| 2 | 1
| 3 | 1
| 1 | 7
| 2 | 8
| 3 | 7
Now I have used this query which gives me no result:
select o.id, o.name from operator as o
where not exists(select * from areas a where id in (1,7,8) and not exists(select * from operatorareas as oa where oa.operatorid=o.id
and oa.areaid = a.id))
I have taken the reference of following link:
SQL query through an intermediate table
I need a guidance regarding structuring of the tables.

Initial Problem
Your Sector/Subsector designation breaks 1NF:
Each domain [column] must be Atomic wrt to the [datatypes available in the] platform.
That is a gross Normalisation error, which will have horrendous consequences downstream. The correction is:
Sector is one datum, one column, eg. Sector 1
SubSector is a separate datum, a separate column, eg. a, b, c
The Data • What is it ?
I need a guidance regarding structuring of the tables.
Ok. But what the data actually means, is not at all clear.
From the little info you have given, the following Predicates can be derived:
Each Operator is assigned to 0-or-1 Area
Assumption: an Operator cannot be in more than one place at a time
Assumption: an Operator may not be assigned
Each [assigned] Area is one of { Sector | SubSector | Unassigned }
AreaType is the Discriminator
Each Sector comprises 0-to-n SubSectors
Each Sector is occupied by 0-to-n Operators
Each SubSector is occupied by 0-to-n Operators
Please check and ensure that each is true (otherwise the data model is garbage).
Relational Data Model
Assuming those Predicates are correct, the Normalised Relational data model is:
Subtype • Exclusive
operators are working in either full sector or any of the sub sectors
What you are seeking in Logic terms is an OR Gate, in Relational terms, it is an Exclusive Subtype
Refer to Subtype for full details on Subtype implementation.
Note • Notation
All my data models are rendered in IDEF1X, the Standard for modelling Relational databases since 1993
My IDEF1X Introduction is essential reading for beginners or novices, wrt a Relational database.
The Query • What is it ?
Now I have used this query which gives me no result
We do not know what result set you are attempting to obtain.
At this point, it does not appear to be related to the linked Question & Answer.
Please explain what result set you would like to obtain, in English. Hopefully observing the given data model.
Supplying the required SQL would then a simple matter.
Enjoy. Please feel free to ask specific questions.

Related

Filtering Columns in PLSQL

I have a table with tons and tons of columns and I'm trying to select only certain columns based on the data the columns contain. The table is part of an application I'm building in Oracle APEX and looks something like this:
|Row Header|Criteria 1|Criteria 2| Criteria 3 | Criteria 4 |Criteria 5 |
|Category | Type A | Type B | Type B | Type A | Type A |
| ID | 2.3 | 2.4 | 2.5 | 3.1 | 3.2 |
| Part A | Yes | Yes | Yes | No | Yes |
| Part B | Yes | No | Yes | Yes | Yes |
| Part C | No | Yes | Yes | Yes | No |
It goes on like this for around 1000ish criteria and 100ish parts I need to find a way to select all the columns that are of a specific type to its own table using SQL.
Id Like the return to look like this:
|Row Header|Criteria 1|Criteria 5 |
|Category | Type A | Type A |
| ID | 3.1 | 3.2 |
| Part A | No | Yes |
| Part B | Yes | Yes |
| Part C | Yes | No |
This way I only have the columns showing that are part of the "Type A" Category and have an ID greater than 3.
I've looked into GROUP BY and FILTER functions that SQL has to offer as well as PIVOT and I don't believe these will help me, but I'd be happy to be proven wrong.
In a relational database, columns are meant to be discrete, non-repeating attributes of a thing. Rows are meant to be multiple instances of that thing. Your table is reversed, using columns for what should be rows, and rows for what should be columns. Another factor is that Oracle limits you to 1000 columns, and you start undergoing severe performance degradation when you exceed 254 columns. Tables simply weren't meant to have hundreds, let alone thousands, of columns. So first step is to pivot your table like this:
Criteria_No, Cat, ID, PtA, PtB, PtC
---------------------------------------------
Row 1: Criteria 1, Type A, 2.3, Yes, Yes, No
Row 2: Criteria 2, Type B, 2.4, Yes, No, Yes
Row 3: Criteria 3, Type B, 2.5, Yes, Yes, Yes
. . . thousands more
But even then, you mentioned that you have 100s of "parts", so Parts A, B, C aren't the only three - the series continues. If so, it would be a violation of normal form to have such a repeating list in a single row. So you have one more step to fix your design: Break this into three tables.
CRITERIA
Criteria_No, Cat, ID
---------------------------------------------
Row 1: Criteria 1, Type A, 2.3
Row 2: Criteria 2, Type B, 2.4
Row 3: Criteria 3, Type B, 2.5
PARTS
Part, anything-else-about-part
-----------------
Part A, blah
Part B, blah,
Part C, blah
. . .
And now the bridge table between them:
CRITERIA_PARTS
Criteria_No, Part
-----------------
1, Part A
1, Part B
1, Part C
2, Part A,
2, Part B,
. . . and so on
You should also place a foreign key on each of the bridge table columns to point to their respective parent tables to ensure data integrity.
Now you query by joining the tables together in your SQL.
Updated: you asked how to move data into this new criteria table from your existing one. Use dynamic SQL like this:
BEGIN
FOR i IN 1..1000
LOOP
EXECUTE IMMEDIATE 'INSERT INTO criteria (criteria_no,cat,id) SELECT criteria_'||i||',category,id FROM oldtable';
END LOOP;
COMMIT;
END;
But of course set the 1000 to the real # of category_n columns.

Access query fails when WHERE statement is added to subquery referencing ODBC link

Original post
Given two tables structured like this:
t1 (finished goods) t2 (component parts)
sku | desc | fcst sku | part | quant
0001 | Car | 10000 0001 | wheel | 4
0002 | Boat | 5000 0001 | door | 2
0003 | Bike | 7500 0002 | hull | 1
0004 | Shirt | 2500 0002 | rudder | 1
... | ... | ... 0003 | wheel | 2
0005 | rotor | 2
... | ... | ...
I am trying to append wheel requirements to the forecast, while leaving all records in the forecast. My results would look like this:
sku | desc | fcst | wheels | wheelfcst
0001 | Car | 10000 | 4 | 40000
0002 | Boat | 5000 | |
0003 | Bike | 7500 | 2 | 15000
0004 | Shirt | 2500 | |
... | ... | ... | ... | ...
The most efficient way to go about this in my eyes is something like this query:
SELECT
t1.sku,
t1.desc,
t1.fcst,
q.quant as wheels,
t1.fcst * q.quant as wheelfcst
FROM
t1
LEFT JOIN
(
SELECT *
FROM t2
WHERE part LIKE "wheel"
)
as q
ON t1.sku = q.sku
The problem is that it gives a very elaborate Invalid Operation. error when ran.
If I remove the WHERE statement: I get the wheel parts as desired but I also pull door, hull, and rudder quantities.
If I move the WHERE statement to the main query (WHERE q.part LIKE "wheel"): I only see goods that contain wheels, but boats are then missing from the results.
I have considered a UNION statement, taking the results of the previously mentioned moving the WHERE out of the subquery (WHERE q.part LIKE "wheel"), but there doesn't seem to be a good way to grab every final item that doesn't have a wheel component because each sku can have anywhere from 0 to many components.
Is there something I'm overlooking in my desired query, or is this something requiring a UNION approach?
EDIT #1 - To answer questions raised by Andre
The full error message is Invalid operation.
sku is the primary key of t1, and there are 1426 records.
t2 contains ~446,000 records, the primary key is a composite of sku and part.
The actual WHERE statement is a partial search. All "wheels" have the same suffix but different component item numbers.
Additionally, I am in Access 2007, it may be an issue related to software version.
Making the subquery into a temporary table works, but the goal is to avoid that procedure.
EDIT #2 - A flaw in my environment
I created a test scenario identical to the one I have posted here, and I get the same results as Andre. At this point, combining these results with the fact that the temporary table method does in fact work, I am led to believe that it is an issue with query complexity and record access. Despite the error message not being the typical Query is too complex. message.
EDIT #3 - Digging deeper into "Complexity"
My next test will be to make the where clause simpler. Sadly, the systems I work on update at lunch each day and I currently cannot reach any data servers. I hope to update my progress at a later point today.
EDIT #4 - Replacing the partial search
Ok, we're back from a meeting and ready to go. I've just ran six queries with three different WHERE clauses:
WHERE part LIKE "*heel" / WHERE component_item LIKE "*SBP" (Original large scale issue)
Works in small scale test, Invalid operation on large scale.
WHERE part LIKE "wheel" / WHERE component_item LIKE "VALIDPART" (Original small scale)
Works in small scale test, Invalid operation on large scale.
WHERE part LIKE "wh33l" / WHERE component_item LIKE "NOTVALIDPART"(Where statements that do not return any records)
Small Scale
sku | desc | fcst | wheels | wheelfcst
0001 | Car | 10000 | |
0002 | Boat | 5000 | |
0003 | Bike | 10000 | |
0004 | Shirt | 5000 | |
Large Scale
sku |description |forecast |component_item |dip_rate
#####|RealItem1 | ###### | |
#####|RealItem2 | ###### | |
#####|RealItem3 | ###### | |
... |... | ... | |
Tl;dr The filter specifics did not make a difference unless the filter resulted in a subquery that returned 0 records.
EDIT #5 - An interesting result
Under the idea of trying every possible solution and test everything I can, I made a local temporary table which contained every field and every record from t2 (~25MB). Referencing this table instead of the ODBC link to t2 works with the partial search query (WHERE component_item LIKE "*SBP"). I am updating the title of this question to reflect that the issue is specific to a linked table.
I copied the sample data to Access 2010 and ran the query, it worked without problems.
What is the full error message you get? ("very elaborate Invalid Operation.")
How many records are in your tables?
Is sku primary key in t1?
In any case, I suggest changing the subquery to:
SELECT sku, quant
FROM t2
WHERE part = "wheel"
LIKE is only needed for partial searches.
A workaround
I have devised a set of queries that works by using one of my original hunches of a UNION statement. Hopefully this allows for anyone that stumbles across this issue to save a bit of time.
Setting up the UNION
My initial problem was with the idea that there was no way to select only wheel record and then join them to null records, because (using t1 as an example) the Boat has parts in t2, but none of them are wheels. I had to first devise a method to see which products had wheels without using a filter.
My intermediary solution:
Query: t1haswheel
SELECT
t1.sku,
t1.desc,
t1.fcst,
SUM(
IF(
t2.part = "wheel",
1, 0
) as haswheel
FROM
t1
LEFT JOIN
(
SELECT *
FROM t2
WHERE part LIKE "wheel"
)
as q
ON t1.sku = q.sku
GROUP BY
t1.sku,
t1.desc,
t1.fcst
This query returns every record from t1 followed by a number based on the number of wheel records there are in the part list for that item number. If there are no records in t2 with "wheel" in the field part, the query returns 0 for the record. This list is what I needed for the UNION statement in the original question.
UNION-ing it all together
At this point, all that is needed is a UNION which uses the summation field from the previous query (haswheel).
SELECT
q1.sku,
q1.desc,
q1.fcst,
t2.quant,
q1.fcst * t2.quant as wheelfcst
FROM
t1haswheel as q1
LEFT JOIN t2
ON q1.sku = t2.sku
WHERE
q1.haswheel > 0 AND
t2.part = "wheel"
UNION ALL
SELECT
q1.sku,
q1.desc,
q1.fcst,
null,
null
FROM
t1haswheel as q1
WHERE q1.haswheel = 0
This pulls in the correct results from records with wheels, and then attaches the records without wheels, while never using the WHERE statement in a subquery which references an ODBC linked table:
sku | desc | fcst | wheels | wheelfcst
0001 | Car | 10000 | 4 | 40000
0003 | Bike | 7500 | 2 | 15000
... | ... | ... | ... | ...
0002 | Boat | 5000 | |
0004 | Shirt | 2500 | |
... | ... | ... | ... | ...

How to select with bitwise flag values in SQL

I have two tables in a SQL Server DB. One table BusinessOperations has various information about this business object, the other table OperationType is purely a bitwise flag table that looks like this:
| ID | Type | BitFlag |
| 1 | Basic-A | -2 |
| 2 | Basic | -1 |
| 3 | Type A | 0001 |
| 4 | Type B | 0002 |
| 5 | Type C | 0004 |
| 6 | Type D | 0008 |
| 7 | Type E | 0016 |
| 8 | Type F | 0032 |
The BitFlag column is a varchar column, the bitflags were inserted as '0001' as an example. In the BusinessOperations table, there's a column where the application that uses these tables updates it based on what is selected in the application's UI. As an example, I have one type which has the Basic,Type A, and Type B types selected. The column value in BusinessOperations is 3.
Based on this, I am trying to write a query which will show me something like this:
| ID | Name | Description | OperationType |
| 1 | Test | Test | Basic, Type A, Type B |
Here is the actual layout of the BusinessOperations table (Basic-A and Basic are bit columns:
| ID | Name | Description | Basic-A | Basic | OperationType |
| 1 | Test | Test | 0 | 1 | 3 |
There is nothing that relates these two tables to each other, so I cannot perform a join. I am very inexperienced with bitwise operations and am at a loss on how exactly to structure my select query which is being used to analyze this data. I feel like it needs a STUFF or CASE, but I don't know how I can get this to just show the types and not just the resultant BitFlag.
SELECT ID, Name, Description, OperationType
FROM OperationType
ORDER BY ID
Since you're storing the flag in OperationType as a VARCHAR, the first thing you need to do to is CONVERT or CAST the string to a number so we can do proper bitwise comparisons. I'm slightly unfamiliar with SQL Server, but you may need to remove the leading zeroes before the cast. Thus, the OperationType column in our desired SQL will look something like
CONVERT(INT, BitFlag)
Then, comparing that to our OperationType column would look something like
CONVERT(INT, BitFlag) & OperationType
The full query would look something like (forgive my lack of SQL Server expertise again):
SELECT bo.ID, bo.Name, bo.Description, ot.Type
FROM BusinessOperations AS bo
JOIN OperationType AS ot
ON CONVERT(INT, ot.BitFlag) & OperationType <> 0
The above query will effectively get you a list of the OperationTypes. If you absolutely need them on one line, see other answers to learn how to emulate something like GROUP_CONCAT in SQL Server. Disclaimer: Joining on a bitmask gives no guarantee of performance.
The other problem this answer does not solve is that of your legacy Basic and Basic-A fields. Personally, I'd do one of two things:
Remove them from the OperationType table and have the application tack the two on, based on the Basic and Basic-A columns as appropriate.
Put Basic and Basic-A as their own, positive flags in the OperationType table, and have the application populate the legacy columns as well as the OperationType column as appropriate.
As Aaron Bertrand has said in the comments, this really isn't an issue for Bitmasking at all. Having a many-many table that associates BusinessOperations.ID to OperationType.ID would solve all your problems in a much better way.
In the BusinessOperations table the Basic-A and Basic field are bit fields which is just another way of saying the value can only be a 1 or 0. Think of it like a boolean value True/False. So, in your query you can check each of those to determine whether to include 'Basic-A' and 'Basic' or not.
The OperationType is probably an id which you can lookup in the OperationsType table to get the Type and BitFlag. Without understanding your data completely it looks as if you could do a join for that part. Hopefully that is in the right general direction. If not, let me know.

MS access 2007 - checklist options(multiple) to be stored in a column of the database

I have a situation like - the customer form in MS access 2007 have list of documents provided by customers. The list is in the checklist format. Assuming there are 6 documents under the checklist. So if the one or more checklists are selected, all the selected list should be saved in the database column named "Documents_Provided". So in order to achieve this scenario what should I have to do. How should my database field "Documents_provided" should be declared and what do I have to write in VBA code.
As per your Question heading suggests "Multiple to be stored in a Column of the database" is a very bad table design, it breaks one of the rules of Fundamentals of Database Design, Data should be atomic.
The system you should be having is a One to Many, between the Customer and Document table. The Customer table will normally have the basic customer information; one side of the relationship, and the Documents table will have all the documents that pertain to each Customer; many side of the relationship. In Addition you will have another table Document Category that will say what are all the documents that needs/can have for each customer. So sample data in your table will be something like,
tbl_Customers
`````````````
ID | customerName | customerArea
----+-------------------+------------------
1 | Paul | Bournemouth
2 | Eugin | Bristol
3 | Francis | London
tbl_DocumentsCategory
`````````````````````
ID | DocumentName
----+---------------------------
1 | Address Proof
2 | Photo ID
3 | Employer Certificate
tbl_CustomersDocument
`````````````````````
ID | CustomerID | DocumentID
----+---------------+--------------
1 | 1 | 1
2 | 1 | 2
3 | 1 | 3
4 | 2 | 1
5 | 2 | 3
6 | 3 | 2
So when you need to get the list of Documents each Customer has, you simply JOIN the two tables to get the right information. This is the standard and efficient way to organize the data. I hope this helps, and you stick to this.

How to properly group SQL results set?

SQL noob, please bear with me!!
I am storing a 3-tuple in a database (x,y, {signal1, signal2,..}).
I have a database with tables coordinates (x,y) and another table called signals (signal, coordinate_id, group) which stores the individual signal values. There can be several signals at the same coordinate.
The group is just an abitrary integer which marks the entries in the signal table as belonging to the same set (provided they belong to the same coordinate). So that any signals with the same 'coordinate_id' and 'group' together form a tuple as shown above.
For example,
Coordinates table Signals table
-------------------- -----------------------------
| id | x | y | | id | signal | coordinate_id | group |
| 1 | 1 | 2 | | 1 | 45 | 1 | 1 |
| 2 | 2 | 5 | | 2 | 95 | 1 | 1 |
| 3 | 33 | 1 | 1 |
| 4 | 65 | 1 | 2 |
| 5 | 57 | 1 | 2 |
| 6 | 63 | 2 | 1 |
This would produce the tuples (1,2 {45,95,33}), (1,2,{65,57}), (2,5, {63}) and so on.
I would like to retrieve the sets of {signal1, signal2,...} for each coordinate. The signals belonging to a set have the same coordinate_id and group, but I do not necessarily know the group value. I only know that if the group value is the same for a particular coordinate_id, then all those with that group form one set.
I tried looking into SQL GROUP BY, but I realized that it is for use with aggregate functions.
Can someone point out how to do this properly in SQL or give tips for improving my database structure.
SQLite supports the GROUP_CONCAT() aggregate function similar to MySQL. It rolls up a set of values in the group and concatenates them together comma-separated.
SELECT c.x, c.y, GROUP_CONCAT(s.signal) AS signal_list
FROM Signals s
JOIN Coordinates ON s.coordinate_id = c.id
GROUP BY s.coordinate_id, s.group
SQLite also permits the mismatch between columns in the select-list and columns in the group-by clause, even though this isn't strictly permitted by ANSI SQL and most implementations.
personally I would write the database as 3 tables:
x_y(x, y, id) coords_groups(pos, group, id) signals(group, signal)
with signals.group->coords_groups.id and coords_groups.pos->x_y.id
as you are trying to represent a sort-of 4 dimensional array.
then, to get from a couple of coordinates (X, Y) an ArrayList of List of Signal you can use this
SELECT temp."group", signals.signal
FROM (
SELECT cg."group", cg.id
FROM x_y JOIN coords_groups AS cg ON x_y.id = cg.pos
WHERE x_y.x=X AND x_y.y=Y )
AS temp JOIN signals ON temp.id=signals."group"
ORDER BY temp."group" ASC
(X Y are in the innermost where)
inside this sort of pseudo-code:
getSignalsGroups(X, Y)
ArrayList<List<Signals>> a
List<Signals> temp
query=sqlLiteExecute(THE_SQL_SNIPPET, x, y)
row=query.fetch() //fetch the first row to set the groupCounter
actualGroup=row.group
temp.add(row.signal)
for(row : query) //foreach row add the signal to the list
if(row.group!=actualGroup) //or reset the list if is a new group
a.add(actualGroup, temp)
actualGroup=row.group; temp= new List
temp.add(row.signal)
return a