First let me say that yes, this is a horrible way to have stored data, second, it isn't my fault :) I am trying to integrate with a 3rd party database to extract info which is stored in 3 tables, which really should have been in two AND stored where table 2 had a many to one relationship. Since that isn't the case, I have a puzzle to share.
Table one contains rows in which multiple values can be stored. Each row has codeid1-codeid20. These columns may contain a value, or a 0 (they are never null). They also have a corresponding codetype1-codetype20 which will be either 0 or 1.
If codetype1 is equal to 0, we go to table 2 and select description from the matching table1.codeid1=table2.id. If codetype1 equals 1, we now have to look at table3 and find where table1.codeid1=table3.id and then match table3.table2id=table2.id and return the description.
Here is the data structure:
table1
codeid1,codeid2,codeid3,...codeid20 ... codetype1,codetype2,codetype3,.....codetype20
18 13 1 33 0 0 1 1
13 21 45 0 0 1 0 0
table2
id, description
13 Item 13 Description
15 Item 15 Description
17 Item 17 Description
18 Item 18 Description
21 Item 21 Description
28 Item 28 Description
45 Item 45 Description
table3
id, table2id
1 15
33 17
21 28
The results I would be looking for would look like this:
rowid, description
1 Item 18 Description
1 Item 13 Description
1 Item 15 Description
1 Item 17 Description
2 Item 13 Description
2 Item 28 Description
2 Item 45 Description
I got started working with someone last night, but I had missed part of the complexity of my situation in not integrating table3. Like I said, fun puzzle... This gives me the relationship between the first 2 tables, but I am unsure how I can work in a 3rd table.
SELECT table1.rowid, table2.description
FROM table2
INNER JOIN table1
ON table2.id=table1.codeie1
OR table2.id=table1.codeie2
...
The database is a Faircom C-Tree DB over an ODBC connection, which is generally compatible with Mysql statements including UNION, WITH, INTERSECT, EXISTS, JOIN... There is no PIVOT function.
https://docs.faircom.com/doc/sqlref/sqlref.pdf
Perhaps it will work with or rather than in:
where exists (select 1
from table1 as t1
where t2.id = t1.codeid1 or
t2.id = t2.codeid2 or
. . .
);
I'm just stuck with this issue atm and I'm not 100% sure how to deal with it.
I have a table where I'm aggregating data on week
select week(create_date),count(*)
from user
where create_date > '2015-02-01'
and id_customer between 9 and 17
group by week(create_date);
the results that I'm getting have missing values in the count, as shown below
5 334
6 376
7 394
8 405
9 504
10 569
11 709
12 679
13 802
14 936
15 1081
16 559
21 1
24 9
25 22
26 1
32 3
34 1
35 1
For example here from 16 to 21 there a obviously 4 values missing I would like these values to be included and count to be 0. I want this because I want the weeks to be matching with other metrics as we are outputting them in an excel file for internal analysis.
Any help would be greatly appreciated.
The problem is that an sql query cannot really produce data that is not there at all.
You have 3 options:
If you have data for each week in your entire table for the period you are querying, then you can use a self join to get the missing weeks:
select week(t1.create_date), count(t2.id_customer)
from customer t1
left join customer t2 on t1.id_customer=t2.id_customer and t1.create_date=t2.create_date and t2.id_customer between 9 and 17
where t1.create_date > '2015-02-01'
group by week(t1.create_date)
If you have missing weeks from the customer table as whole, then create a helper table that contain week numbers from 1 or 0 (depending on mysql config) to 53 and do a left join to this helper table.
Use a stored procedure that loops through the results of your original query and inserts the missing data in the resultset using a temporary table and then returns the extended dataset as result.
The problem is that there is no data matching your criteria for the missing weeks. A solution will be to join from a table that has all week numbers. For example if you create a table weeknumbers with one field weeknumber containing all the numbers from 0 to 53 you can use something like this
select weeknumber,count(user.*)
from weeknumbers left join user on (weeknumbers.weeknumber=week(user.create_date)
and user.create_date > '2015-02-01'
and user.id_customer between 9 and 17)
group by weeknumber;
Additionaly you might want to limit the week numbers you do not want to see.
The other way is to do it in the application.
Table reserve
sib boot_id day
22 101 10/10/1996
22 103 11/12/1996
Table Sailors
sid sname rating age
22 dustin 7 45.0
31 lubber 8 55.5
58 rusty 10 35.0
28 yuppy 9 45.0
44 gruppy 8 55.5
The result I want is find name who borrow all boot ?
You should definetely try a basic tutorial, there is a good one at Khan Academy: https://www.khanacademy.org/computing/computer-programming/sql.
It is a simple join. Since one person has both items, I've also included a distinct not to repeat the name.
Your answer is :
select distinct ts.sname
from Table_Sailors as ts
inner join Table_Reserve as tr on ts.sid = tr.sib
Just change the table names, Table_Sailors and Table_Reserve, to whatever table name you have in your database.
So Here is My data
ID C1 C2 C3
6 Digit 2 6,8,10,12
12 Digit 3 15
15 127 Digit 2 6,7,8,9,10,11,12,13
68 140,141 Digit 11 85,86,87,88,167,168,158,159
73 1 Digit 11 85,86,87,88,169,170
76 Digit 11 85,86,87,91,164,165,166,167,168
99 Digit 11 20,27,85,86,87
106 Digit 1 1,2
111 Digit 11 85,86,87,88
112 Digit 11 85,86,87,88
135 Digit 11 85,86,87
and my condition string is (2,6,15,37,42,52,62,65,79,85,94,100,104,107,113,124,131)
Now,I want to exclude row 3,4,5 if the values 127,140,141,1 are not in the list condition. I tried Not in , but no avail. I think I might be missing something basic, but just cant get it.
It's better not to store multiple values in a column if possible. Then it's easier to do queries like this.
You cannot use "IN" or "NOT IN" because they are looking for a list of separate items. But C3 is just one item that happens to have commas in it.
Try this:
SELECT * FROM
(SELECT ID, C1, C2, CONCAT('|',REPLACE(C3,',','|'),'|') as C3 FROM `table` WHERE `C3` ) as t1
WHERE t1.C3 NOT LIKE "|127|" AND t1.C3 NOT LIKE "|140|" AND t1.C3 NOT LIKE "|141|" AND t1.C3 NOT LIKE "|1|"
You could avoid the "|" and just concat "," to the start and end.
Or you could fix your database schema so that it actually acts like a Normalized Relational Database.
Every column that contains multiple values should be separated out into its own table.
There should be no column C3 in your table above. Instead, you should have a table, some_other_data:
At this point, I see that C3=6 is related to more than one record in the main table. Therefore, you actually need a third, linking table, in addition to some_other_data. See below.
`some_other_data`
id
6
8
10
12
15
`main_table_to_some_other_data_link`
some_other_data_id | main_table_id
6 6
8 6
10 6
12 6
15 12
6 15
etc. You can see that the linking table can contain duplicates of either value. But your other two tables would have completely unique ids.
I think you're trying to solve the wrong problem.
(I'm assuming you can change your table structure. If you can't someone else will need to address your question.)
The long lists of comma-separated data are a flag that they have a one-to-many relationship with ID.
For example, make the data in C3 its own table:
ID MainID C3
================
1 6 6
2 6 8
3 6 10
4 6 12
5 12 15
6 15 6
7 15 7
8 15 8
9 15 9
10 15 10
11 15 11
12 15 12
13 15 13
// and so forth //
So ID is the primary key of the new table, MainID is the foreign key that refers to the record in your primary table, and C3 is the data in C3.
Each separate value of C3 now has its own record.
Now, you're in a position to use something like
Select * from MainTable
Inner Join NewTable
On MainTable.ID = NewTable.MainID
Where NewTable.C3 Not In (2,6,15,37,42,52,62,65,79,85,94,100,104,107,113,124,131);
If you can, pulling out the one-to-many relationships into their own tables will make things easier for you.
Apologies if this question is a bit long, but I wanted to explain in detail what it is I am trying to do.
I am developing a database in MS Access 2010/Windows 7 which analyses and reports on incidents (e.g. faults) in an organisation. An incident is reported as beginning at a particular date/time in a particular location for a particular duration. An incident may occasionally cause one or more "live resilience outages" (LRO) which will have the same start-time but can be in different locations and have different durations. So for example a router going out of service in the central technical area for 600 sec might cause live outages of 60 sec and 30 sec in studios 5 and 6 respectively.
I need to report on three date ranges: the month in question, the previous month and the (financial, beginning in April) year to date. So for example the report for March 2012 would consider the periods 01 Mar 2012 - 31 Mar 2012 (month), 01 Feb 2012 - 29 Feb 2012 (previous) and 01 Apr 2011 - 31 Mar 2012 (YTD).
These dates are correctly calculated in a form called ReportCentre. I have three queries to return the LROs for the different date ranges: QueryLROMonth, QueryLROPrevious and QueryLROYTD all of which work properly in isolation (i.e. return the correct values). So for example QueryLROMonth is defined as
SELECT lro.*
FROM lro INNER JOIN incidents ON lro.pid = incidents.id
WHERE (((incidents.begin) Between [Forms]![ReportCentre].[StartMonth] And
[Forms]![ReportCentre].[EndMonth]));
which returns the expected values:
id pid duration facility
6 681 30 23
7 686 857 23
8 735 600 25
9 738 600 25
as does the YTD query
id pid duration facility
1 100 120 25
2 366 5 25
3 380 460 1
4 505 341 23
5 622 0 29
6 681 30 23
7 686 857 23
8 735 600 25
9 738 600 25
20 1297 50 1
So far so good, but now the bit that's got me puzzled. I am trying to design another query which takes the output of the three LRO queries (and some other data), groups it all by facility and calculates things like availability. If I design a totals query and include the Facilities table (for the facility name) and the QueryLROMonth query e.g.
SELECT facilities.facility, Count(QueryLROMonth.id) AS lrocountmonth, Sum(QueryLROMonth.duration) AS lrosecondsmonth
FROM QueryLROMonth INNER JOIN facilities ON QueryLROMonth.facility = facilities.ID
GROUP BY facilities.facility;
This works fine and produces what I expect.
facility lrocountmonth lrosecondsmonth
HQ3 2 887
HQ5 2 1200
but as soon as I introduce the YTD query:
SELECT facilities.facility, Count(QueryLROMonth.id) AS lrocountmonth, Sum(QueryLROMonth.duration) AS lrosecondsmonth, Count(QueryLROYTD.id) AS lrocountytd, Sum(QueryLROYTD.duration) AS lrosecondsytd
FROM QueryLROYTD INNER JOIN (QueryLROMonth INNER JOIN facilities ON QueryLROMonth.facility = facilities.ID) ON QueryLROYTD.facility = facilities.ID
GROUP BY facilities.facility;
for some reason stuff starts being counted reported wrongly. Specifically the two Count columns are multiplied together and so lrocountmonth and lrosecondsmonth are both multiplied by lrocountytd. Similarly lrocountytd and lrosecondsytd are both multiplied by lrocountmonth.
facility lrocountmonth lrosecondsmonth lrocountytd lrosecondsytd
HQ3 6 2661 6 2456
HQ5 8 4800 8 2650
What am I doing wrong? How do I prevent this entanglement?
Your [QueryLROMonth] and [QueryLROYTD] queries each return multiple rows per Facility, but because you are effectively JOINing them on just the Facility_ID you are producing an OUTER JOIN of sorts. For example, if for a given Facility your [Month] query contains 3 rows and your [YTD] query contains 6 rows then your JOIN on Facility_ID alone will produce 18 rows.
You'll want to create aggregation queries that "roll up" the Monthly and YTD numbers by Facility first, so they each have only one row per Facility. You can then use them in your final query to produce the report.
Troubleshooting tip: If your aggregation queries are producing strange results try removing the GROUP BY parts so you can see the underlying rows that are being aggregated.