Is it possible to programmatically edit a SQLite CREATE VIEW statement? - sql

I have a view that contains a number of nested views as shown below.
Main View:
CREATE VIEW `qryAttackRate` AS
Select qryFoodInCases.fldCaseID,qryFoodInCases.fldFood,
AteAndGotSick,TotalAte,AteAttackRate,
NotAteAndGotSick,TotalNotAte,NotAteAttackRate,
ROUND(AteAttackRate/CAST(NotAteAttackRate AS FLOAT),2) RelativeRisk
FROM qryFoodInCases
LEFT JOIN qryNotAteAttackRate QA
ON qryFoodInCases.fldFood=QA.fldFood
LEFT JOIN qryAteAttackRate QN
ON qryFoodInCases.fldFood=QN.fldFood
GROUP BY qryFoodInCases.fldFood
ORDER BY RelativeRisk Desc
These are 2 of the sub views. There are a few more:
CREATE VIEW `qryAteAttackRate`
AS SELECT qryFoodInCases.fldCaseID,qryFoodInCases.fldFood,
COALESCE(qryAteAndGotSick.AteAndGotSick,0) AteAndGotSick,
qryFoodInCases.fldFoodFrequency TotalAte,
100*COALESCE(qryAteAndGotSick.AteAndGotSick,0)/
qryFoodInCases.fldFoodFrequency AteAttackRate
FROM qryFoodInCases
LEFT JOIN qryAteAndGotSick
ON qryFoodInCases.fldFood=qryAteAndGotSick.fldFood
GROUP BY qryFoodInCases.fldFood
CREATE VIEW `qryFoodInCases`
AS SELECT tblCases.fldCaseID,fldfood,
COUNT(tblFoodHistory.fldFoodID) AS fldFoodFrequency
FROM tblFood
INNER JOIN tblFoodHistory
ON tblFoodHistory.fldFoodID=tblFood.fldFoodID)
INNER JOIN tblMealHistory
ON tblFoodHistory.fldMealID=tblMealHistory.fldMealHistoryID)
INNER JOIN tblInterviews
ON tblInterviews.fldInterviewID=tblMealHistory.fldInterviewID)
INNER JOIN tblCases
ON tblCases.fldCaseID=tblInterviews.fldCaseID
GROUP BY tblCases.fldCaseID,tblFood.fldFood
I want to query the main view for each fldCaseID. Problem is, the WHERE condition when querying the main view does not apply to the subviews.
Other than creating a long complex SQL statement, is there a way to programmatically insert a WHERE condition into the sub views?
To illustrate the problem see the output of select * from qryAttackRate where fldcaseid=1 before and after adding a second case without making any change to data related to the first case
Before
+-----------+------------+---------------+----------+---------------+------------------+-------------+------------------+--------------+
| fldCaseID | fldFood | AteAndGotSick | TotalAte | AteAttackRate | NotAteAndGotSick | TotalNotAte | NotAteAttackRate | RelativeRisk |
+-----------+------------+---------------+----------+---------------+------------------+-------------+------------------+--------------+
| 1 | Beans | 4 | 4 | 100 | 1 | 3 | 33 | 3.03 |
| 1 | Cabagge | 2 | 3 | 66 | 3 | 4 | 75 | 0.88 |
| 1 | fried fish | 2 | 3 | 66 | 3 | 4 | 75 | 0.88 |
| 1 | Banana | 1 | 2 | 50 | 4 | 5 | 80 | 0.62 |
| 1 | Pork | 2 | 4 | 50 | 3 | 3 | 100 | 0.5 |
| 1 | Chicken | 1 | 3 | 33 | 4 | 4 | 100 | 0.33 |
| 1 | Potatoes | 0 | 2 | 0 | 5 | 5 | 100 | 0 |
| 1 | Rice | 0 | 2 | 0 | 5 | 5 | 100 | 0 |
+-----------+------------+---------------+----------+---------------+------------------+-------------+------------------+--------------+
After
+-----------+------------+---------------+----------+---------------+------------------+-------------+------------------+--------------+
| fldCaseID | fldFood | AteAndGotSick | TotalAte | AteAttackRate | NotAteAndGotSick | TotalNotAte | NotAteAttackRate | RelativeRisk |
+-----------+------------+---------------+----------+---------------+------------------+-------------+------------------+--------------+
| 1 | Cabagge | 2 | 3 | 66 | 4 | 7 | 57 | 1.16 |
| 1 | fried fish | 2 | 3 | 66 | 4 | 7 | 57 | 1.16 |
| 1 | Pork | 2 | 4 | 50 | 4 | 6 | 66 | 0.76 |
| 1 | Potatoes | 0 | 2 | 0 | 6 | 8 | 75 | 0 |
+-----------+------------+---------------+----------+---------------+------------------+-------------+------------------+--------------+

A view always computes a fixed result set before any processing in the outer query is applied. (The database actually tries to optimize away unneeded processing in the view, but it will always behave as if the view were materialized in advance.)
It is not possible to change a view (with parameters, or with any other mechanism).
If your subqueries must change dynamically, you cannot use views.
You must write them as subqueries, or (in SQLite 3.8.3 or later) as common table expressions.

Related

How do I get around aggregate function error?

I have the following sql to calculate a % total:
SELECT tblTourns_atp.ID_Ti,
Sum([FS_1]/(SELECT Sum(FSOF_1)
FROM stat_atp
WHERE stat_atp.ID_T = tblTourns_atp.ID_T)) AS S1_IP
FROM stat_atp
INNER JOIN tblTourns_atp ON stat_atp.ID_T = tblTourns_atp.ID_T
GROUP BY tblTourns_atp.ID_Ti
I'm getting the 'aggregate error' because it wants the ID_T fields either grouped or in an aggregate function. I've read loads of examples but none of them seem to apply when the offending field is the subject of 'WHERE'.
Tables and output as follows:
+----------+------+--------+--+---------------+-------+--+--------+--------+
| stat_atp | | | | tblTourns_atp | | | Output | |
+----------+------+--------+--+---------------+-------+--+--------+--------+
| ID_T | FS_1 | FSOF_1 | | ID_T | ID_Ti | | ID_Ti | S1_IP |
| 1 | 20 | 40 | | 1 | 1 | | 1 | 31.03% |
| 2 | 30 | 100 | | 2 | 1 | | 2 | 28.57% |
| 3 | 40 | 150 | | 3 | 1 | | 3 | 33.33% |
| 4 | 30 | 100 | | 4 | 2 | | | |
| 5 | 30 | 100 | | 5 | 2 | | | |
| 6 | 40 | 150 | | 6 | 2 | | | |
| 7 | 20 | 40 | | 7 | 3 | | | |
| 8 | 30 | 100 | | 8 | 3 | | | |
| 9 | 40 | 150 | | 9 | 3 | | | |
| 10 | 20 | 40 | | 10 | 3 | | | |
+----------+------+--------+--+---------------+-------+--+--------+--------+
Since you already have an inner join between the two tables, a separate subquery isn't required:
select t.id_ti, sum(s.fs_1)/sum(s.fsof_1) as pct
from tbltourns_atp t inner join stat_atp s on t.id_t = s.id_t
group by t.id_ti

How to create a query for items that appears only in one list

The scenario I am working on is as follows:-
A number of interviews are conducted a food poisoning case
A query called qryFoodInCase ( fldCaseID, fldFood) is generate consisting of all the food mentioned in all the interviews
An other query call qryFoodInInterview( fldCaseID, fldInterviewID, fldFood) consist of the food mentioned in each Interview
Now I am after the sql for a query that will return the food that was not consumed by an interviewee but consumed by one or more other interviewees.
The closest I've got is:
select Q1.fldCaseID,Q1.fldfood,Q2.fldInterviewID,fldGotSick
from qryFoodInCases as Q1
left join
(select * from qryFoodInInterview where qryFoodInInterview.fldInterviewID=1) as Q2
on Q1.fldFood=Q2.fldFood
where Q1.fldCaseID=1
The field Q2.fldInterviewID returns 1 for the food consumed and null for the food not consumed. However, I don't want to hard code the fldInterviewID in the sql. I would like a similar recordset returned for all the interviews in one query.
The SQL for qryFoodInCase and qryFoodInInterview are as follows:-
CREATE VIEW `qryFoodInCases`
AS
SELECT tblCases.fldCaseID
,fldfood
,count(tblFoodHistory.fldFoodID) AS fldFoodFrequency
FROM tblFood
INNER JOIN tblFoodHistory
ON tblFoodHistory.fldFoodID = tblFood.fldFoodID
INNER JOIN tblMealHistory
ON tblFoodHistory.fldMealID = tblMealHistory.fldMealHistoryID
INNER JOIN tblInterviews
ON tblInterviews.fldInterviewID = tblMealHistory.fldInterviewID
INNER JOIN tblCases
ON tblCases.fldCaseID = tblInterviews.fldCaseID
GROUP BY tblCases.fldCaseID, tblFood.fldFood
OUTPUT:
+-----------+------------+------------------+
| fldCaseID | fldFood | fldFoodFrequency |
+-----------+------------+------------------+
| 1 | Banana | 3 |
| 1 | Beans | 5 |
| 1 | Cabagge | 3 |
| 1 | Chicken | 1 |
| 1 | Pork | 5 |
| 1 | Potatoes | 1 |
| 1 | Rice | 1 |
| 1 | fried fish | 1 |
| 2 | Cabagge | 1 |
| 2 | Chicken | 2 |
| 2 | Potatoes | 1 |
| 2 | Rice | 1 |
| 2 | Salad | 1 |
+-----------+------------+------------------+
and
CREATE VIEW `qryFoodInInterview`
AS
SELECT tblInterviews.fldCaseID
,tblInterviews.fldInterviewID
,tblFood.fldFood
,tblInterviews.fldGotSick
FROM tblInterviews
INNER JOIN tblMealHistory
ON tblInterviews.fldInterviewID = tblMealHistory.fldInterviewID
INNER JOIN tblFoodHistory
ON tblFoodHistory.fldMealID = tblMealHistory.fldMealHistoryID
INNER JOIN tblFood
ON tblFood.fldFoodID = tblFoodHistory.fldFoodID
GROUP BY tblInterviews.fldInterviewID, tblFoodHistory.fldFoodID
OUTPUT
+-----------+----------------+------------+------------+
| fldCaseID | fldInterviewID | fldFood | fldGotSick |
+-----------+----------------+------------+------------+
| 1 | 1 | Pork | 0 |
| 1 | 1 | Banana | 0 |
| 1 | 1 | Rice | 0 |
| 1 | 1 | Potatoes | 0 |
| 1 | 2 | Chicken | 1 |
| 1 | 2 | Banana | 1 |
| 1 | 2 | Beans | 1 |
| 1 | 4 | Pork | 1 |
| 1 | 4 | fried fish | 1 |
| 1 | 4 | Beans | 1 |
| 2 | 6 | Salad | 0 |
| 2 | 6 | Chicken | 0 |
| 2 | 6 | Cabagge | 0 |
| 2 | 6 | Rice | 0 |
| 2 | 6 | Potatoes | 0 |
| 1 | 8 | Pork | 0 |
| 1 | 8 | Cabagge | 0 |
| 1 | 9 | Pork | 1 |
| 1 | 9 | Banana | 1 |
| 1 | 9 | Beans | 1 |
| 1 | 10 | Cabagge | 1 |
| 1 | 10 | Beans | 1 |
| 1 | 11 | Pork | 1 |
| 1 | 11 | Cabagge | 1 |
| 1 | 11 | Beans | 1 |
+-----------+----------------+------------+------------+
SQL Fiddle Demo
create a cross join off all food with all interviewrs
then a left join to see which one didnt had interview
the null mean interview didnt consume food
and exists mean someone different to interview consume food
.
SELECT F.fldFood, I.fldInterviewID, FI.fldInterviewID
FROM qryFoodInCases F, (SELECT DISTINCT fldInterviewID FROM qryFoodInInterview) I
LEFT JOIN qryFoodInInterview FI
ON F.fldFood = FI.fldFood
AND I.fldInterviewID = FI.fldInterviewID
WHERE FI.fldInterviewID IS NULL
AND EXISTS (SELECT 1
FROM qryFoodInInterview Q1
WHERE Q1.fldFood = F.fldFood
AND Q1.fldInterviewID <> I.fldInterviewID)
;
The trick was do the thing one step at the time. Once I realize I need all the combinations and create the cross join, the rest was easy.
select Q1.fldCaseID,Q1.fldfood,Q2.fldInterviewID,fldGotSick
from qryFoodInCases as Q1
left join
(select * from qryFoodInInterview QF1
JOIN qryFoodInInterview QF2
where QF1.fldInterviewID <> QF2.fldInterviewID
AND QF1.fldInterviewID > QF2.fldInterviewID
) as Q2
on Q1.fldFood=Q2.fldFood
where Q1.fldCaseID=1
As you mentioned you want to duplicate the items FoodInterviews, self JOIN will suffice your question. Please try this out , caution I didn't tested it.
Fixing Missed Self Join Condition to fix duplicates.

Join Distinct or First

I have a table structure for SalesItems, and Sales.
SalesItems is setup something like this
| SaleItemID | SaleID | ProductID | ProductType |
| 1 | 1 | 1 | 1 |
| 2 | 1 | 2 | 2 |
| 3 | 1 | 15 | 1 |
| 4 | 2 | 5 | 2 |
| 5 | 3 | 1 | 1 |
| 6 | 3 | 8 | 5 |
And Sales is setup something like this
| Sale | Cash |
| 1 | 1.00 |
| 2 | 10.00 |
| 3 | 28.50 |
I am trying to export a basic 'Daily History' that uses joins to spit out the information like this.
| Date | StoreID | Type1Sales | Type2Sales | ... | Cash Taken |
| 5/2 | 50 | 50 | 40 | ... | 39.50 |
| 5/3 | 50 | 10 | 32.50 | ... | 48.50 |
The issue I'm having is if I do an inner join From Sales to Sales Items, I'll end up with this.
| SaleItemID | SaleID | ProductID | ProductType | Sale | Cash |
| 1 | 1 | 1 | 1 | 1 | 1.00 |
| 2 | 1 | 2 | 2 | 1 | 1.00 |
| 3 | 1 | 15 | 1 | 1 | 1.00 |
| 4 | 2 | 5 | 2 | 2 | 10.00 |
| 5 | 3 | 1 | 1 | 3 | 28.50 |
| 6 | 3 | 8 | 5 | 3 | 28.50 |
So if I do a SUM(Cash), then I'll end up returning $70.00, instead of the correct $39.50. I'm not the best with joins, so I've been researching outer joins and such, but none of those seem to work as it's still matching up. Is there a way to only match on the FIRST instance, and return NULL for the rest? For example, something like this
| SaleItemID | SaleID | ProductID | ProductType | Sale | Cash |
| 1 | 1 | 1 | 1 | 1 | 1.00 |
| 2 | 1 | 2 | 2 | 1 | NULL |
| 3 | 1 | 15 | 1 | 1 | NULL |
| 4 | 2 | 5 | 2 | 2 | 10.00 |
| 5 | 3 | 1 | 1 | 3 | 28.50 |
| 6 | 3 | 8 | 5 | 3 | NULL |
Or do you have any other suggestions for returning back the correct amount of Cash for each particular day?
Use DISTINCT(SaleID) in your SELECT to return a single row for each Sale ID.

trying to write a query with Table A,B where Cond1: A.pc=B.pc & Cond2: (preferred (A.sub = B.Sub) or else any 1 row that meet only Cond1)

I trying to get the result table to contain rows where
Condition1: A.pc=B.pc AND
Condition2: (preferred (A.sub = B.Sub) or
else any one row that satisfy only Condition1)
I have tried the following inner join query and few other join and sub-query but can not figure out exact way to write a query with above strange condition.
SELECT * FROM tblA AS A INNER JOIN tblB AS B
ON A.sub=B.sub
WHERE A.pc=B.pc
tblA
-------------------
| id | pc | sub |
-------------------
| 0 | 5 | abc |
| 1 | 8 | def |
| 2 | 6 | ghi |
| 3 | 2 | jkl |
| 4 | 7 | mno |
| 5 | 19 | pqr |
-------------------
tblB
-------------------------
| pc | sub | uml | ull |
-------------------------
| 3 |arm | 1 | 1 |
| 3 |gtk | 1 | 2 |
| 3 |lmn | 1 | 3 |
| 3 |pop | 1 | 4 |
| 5 |abc | 1 | 5 |
| 5 |hlq | 1 | 6 |
| 5 |pon | 2 | 1 |
| 5 |qrt | 2 | 2 |
| 7 |alo | 2 | 3 |
| 7 |mno | 2 | 4 |
| 7 |ghm | 2 | 5 |
| 7 |stm | 2 | 6 |
| 9 |mck | 2 | 7 |
| 9 |plo | 3 | 1 |
| 9 |rtk | 3 | 2 |
| 9 |ert | 3 | 3 |
| 6 |gji | 3 | 4 |
| 6 |ghi | 3 | 5 |
| 6 |yux | 4 | 1 |
| 6 |del | 4 | 2 |
| 2 |jkl | 4 | 3 |
| 2 |jll | 5 | 4 |
| 2 |uin | 6 | 1 |
| 2 |tro | 6 | 2 |
| 19 |ppm | 6 | 3 |
| 19 |kde | 6 | 4 |
| 19 |grp | 6 | 5 |
| 19 |sho | 6 | 6 |
-------------------------
Expected Result Table:
-------------------------------
| id | pc | sub | uml | ull |
-------------------------------
| 0 | 5 |abc | 1 | 5 |
| 2 | 6 |ghi | 3 | 5 |
| 3 | 2 |jkl | 4 | 3 |
| 4 | 7 |mno | 2 | 4 |
| 5 | 19 |ppm | 6 | 3 | *
-------------------------------
* notice this is a arbitrary row as (A.sub=B.sub) not found
** notice there is no result for id=1 as pc=8 do not exist in tblB
Until someone comes up with a better answer, here is some code that does what you want.
Please, note it might not be a good solution in terms of performance (espcially as your tables grow).
SELECT *
FROM (
SELECT tblA.id, tblB.*
FROM tblA INNER JOIN tblB
ON tblA.pc = tblB.pc AND
tblA.id NOT IN (SELECT tblA.id
FROM tblA INNER JOIN tblB
ON tblA.sub = tblB.sub)
GROUP BY tblA.id
UNION
SELECT tblA.id, tblB.*
FROM tblA INNER JOIN tblB
ON tblA.sub = tblB.sub
GROUP BY tblA.id
) AS tu
ORDER BY id ASC;
See, also, this short demo.
One way of doing it I came up with is to repeat a join condition in where clause:
SELECT *
FROM tblA AS A
INNER JOIN tblB AS B
ON A.pc = B.pc
WHERE A.sub = B.sub
OR A.pc = B.pc

Rails 3 - complex query with joins and counts, possible subqueries?

Ok, so i have a bit of a complex query i am trying to come up with in my rails application. I have four tables: Clients, Projects, Invoices, Invoice_Line_Items. I am trying to get certain bits of data from all of those tables and display it in a "reports" type view in my application. This is what the structures look like for the four tables:
Clients
| id | name | archive |
----------------------------------------
| 1 | Client 1 | 0 |
| 2 | Client 2 | 0 |
Projects
| id | client_id | name | archive |
------------------------------------------------------
| 1 | 1 | Project 1 | 0 |
| 2 | 1 | Project 2 | 1 |
| 3 | 2 | Project 3 | 0 |
| 4 | 2 | Project 4 | 1 |
Invoices
| id | client_id | project_id | name | archive |
----------------------------------------------------------------------
| 1 | 1 | 1 | Invoice 1 | 0 |
| 2 | 1 | 1 | Invoice 2 | 0 |
| 3 | 1 | 2 | Invoice 3 | 1 |
| 4 | 1 | 2 | Invoice 4 | 1 |
| 5 | 2 | 3 | Invoice 5 | 0 |
| 6 | 2 | 3 | Invoice 6 | 0 |
| 7 | 2 | 4 | Invoice 7 | 1 |
| 8 | 2 | 4 | Invoice 8 | 1 |
Invoice_Line_Items
| id | invoice_id | name | amount_due |
---------------------------------------------------------
| 1 | 1 | Item 1 | 500 |
| 2 | 1 | Item 2 | 500 |
| 3 | 2 | Item 3 | 500 |
| 4 | 2 | Item 4 | 500 |
| 5 | 3 | Item 5 | 500 |
| 6 | 3 | Item 6 | 500 |
| 7 | 4 | Item 7 | 500 |
| 8 | 4 | Item 8 | 500 |
| 9 | 5 | Item 9 | 500 |
| 10 | 5 | Item 10 | 500 |
| 11 | 6 | Item 11 | 500 |
| 12 | 6 | Item 12 | 500 |
| 13 | 7 | Item 13 | 500 |
| 14 | 7 | Item 14 | 500 |
| 15 | 8 | Item 15 | 500 |
| 16 | 8 | Item 16 | 500 |
Ok, hope those diagrams make sense enough. What i am looking for as a result set is this (example data set taken from above example data):
| clients.name | current_projects | archived_projects | total_amount_due | total_amount_paid |
-----------------------------------------------------------------------------------------------------------
| Client 1 | 1 | 1 | 2000 | 2000 |
| Client 2 | 1 | 1 | 2000 | 2000 |
Ok, so here's what's going on there:
Getting all non-archived clients
Getting a count of all non-archived projects
Getting a count of all archived projects
Getting a total_amount_due from the invoice_line_items table that is a sum of all of the non-archived invoices
Getting a total_amount_paid from the invoice_line_items table that is a sum of all of the archived invoices
I am relatively new to Rails and this is a fairly complex query (at least in my head). Please let me know if there is a simpler solution that i am overlooking or if i am just over complicating it. If i need to do multiple queries in my controller that's fine, i was just wanting to see if i could get away with one sql call. I'm pretty sure i can do this pretty easily with some subqueries but i'm not sure how to write those in the controller in Rails.
Thanks for any help or direction you can provide and if this question is just outrageous or whatever just let me know and i'll delete it and go search the Googles more (have tried already to no avail).
Ok, well i ended up figuring out a solution myself. Not quite sure it's the best solution....feels heavy and messing but i just created quite a few objects in the controller to get the sql statements i needed to pull the data from the database. I basically have one object for each column (column, not each row). Let me know if anyone can figure out a better solution.