How to write the subquery? - database

I am no sure how to do the following query for the following tables:
1st table - book_type:
book_type_id
book_type_code
book_type_description
2nd table - books:
book_id
book_type_id
book_code
book_status
book_description
3rd table - sold_books:
book_id
price
sold_books status (canceled, or not)
amount_of_books_sold
Books that have an average of amount_of_books_sold that is less than the average of amount_of_books_sold for the book_type that the book is in.
Do not show canceled books from sold_books_status.
Only these columns:
book_type_description, book_type_code, book_description
avg amount_of_books_sold, avg amount_of_books_sold forsook_type
First I tried to do all in one query. Then I tried using with statement. It didn't show me an error, but nothing was shown.
Then I tried with subqueries. No success, because I didn't know what to put in subquery.
Any ideas how to create this query or subquery in sqlite ?

There're a few things need to be clarified:
What's the relationship between books and sold_books?
Does avg_amount_of_books_sold include sold_book_status='canceled'?
Assume that
books and sold_books is 1-to-1 (This means book_id in sold_books is a primary key and also a foreign key).
avg_amount_of_books_sold does not include sold_book_status='canceled'
Then we can do this in a few steps:
Step 1. Calculate avg_amount_of_books_sold (please remove where... clause if canceled books are included.)
select avg(amount_of_books_sold) as avg_amount_of_books_sold
from sold_books
where sold_book_status <> 'canceled';
Step 2. Join all three tables to get those columns you need.
select t.book_type_description,
t.book_type_code,
b.book_description,
s.amount_of_books_sold
from book_type t
join books b
on t.book_type_id = b.book_type_id
join sold_books s
on b.book_id = s.book_id
where s.sold_book_status <> 'canceled';
Step 3. Put the above two queries in CTE sub-query and then compares amount_of_books_sold and avg_amount_of_books_sold in where clause to include only amount_of_books_sold < avg_amount_of_books_sold.
with cte_avg_amount_of_books_sold as (
select avg(amount_of_books_sold) as avg_amount_of_books_sold
from sold_books
where sold_book_status <> 'canceled'),
cte_sold_books_not_canceled as (
select t.book_type_description,
t.book_type_code,
b.book_description,
s.amount_of_books_sold
from book_type t
join books b
on t.book_type_id = b.book_type_id
join sold_books s
on b.book_id = s.book_id
where s.sold_book_status <> 'canceled')
select t.*, a.*
from cte_sold_books_not_canceled t, cte_avg_amount_of_books_sold a
where t.amount_of_books_sold < a.avg_amount_of_books_sold;
Please note that if the relationship between books and sold_books is not 1-to-1 but 1-to-many, then you need to have a rule about either how to pick a row from sold_books for a specific book_id or how to aggregate the information in sold_books to book_id level.

Related

SQL Server FullText Search with Weighted Columns from Previous One Column

In the database on which I am attempting to create a FullText Search I need to construct a table with its column names coming from one column in a previous table. In my current implementation attempt the FullText indexing is completed on the first table Data and the search for the phrase is done there, then the second table with the search results is made.
The schema for the database is
**Players**
Id
PlayerName
Blacklisted
...
**Details**
Id
Name -> FirstName, LastName, Team, Substitute, ...
...
**Data**
Id
DetailId
PlayerId
Content
DetailId in the table Data relates to Id in Details, and PlayerId relates to Id in Players. If there are 1k rows in Players and 20 rows in Details, then there are 20k rows in Data.
WITH RankedPlayers AS
(
SELECT PlayerID, SUM(KT.[RANK]) AS Rnk
FROM Data c
INNER JOIN FREETEXTTABLE(dbo.Data, Content, '"Some phrase like team name and player name"')
AS KT ON c. DataID = KT.[KEY]
GROUP BY c.PlayerID
)
…
Then a table is made by selecting the rows in one column. Similar to a pivot.
…
SELECT rc.Rnk,
c.PlayerID,
PlayerName,
TeamID,
…
(SELECT Content FROM dbo.Data data WHERE DetailID = 1 AND data.PlayerID = c.PlayerID) AS [TeamName],
…
FROM dbo.Players c
JOIN RankedPlayers rc ON c. PlayerID = rc. PlayerID
ORDER BY rc.Rnk DESC
I can return a ranked table with this implementation, the aim however is to be able to produce results from weighted columns, so say the column Playername contributes to the rank more than say TeamName.
I have tried making a schema bound view with a pivot, but then I cannot index it because of the pivot. I have tried making a view of that view, but it seems the metadata is inherited, plus that feels like a clunky method.
I then tried to do it as a straight query using sub queries in the select statement, but cannot due to indexing not liking sub queries.
I then tried to join multiple times, again the index on the view doesn't like self-referencing joins.
How to do this?
I have come across this article http://developmentnow.com/2006/08/07/weighted-columns-in-sql-server-2005-full-text-search/ , and other articles here on weighted columns, however nothing as far as I can find addresses weighting columns when the columns were initially row data.
A simple solution that works really well. Put weight on the rows containing the required IDs in another table, left join that table to the table to which the full text search had been applied, and multiply the rank by the weight. Continue as previously implemented.
In code that comes out as
DECLARE #Weight TABLE
(
DetailID INT,
[Weight] FLOAT
);
INSERT INTO #Weight VALUES
(1, 0.80),
(2, 0.80),
(3, 0.50);
WITH RankedPlayers AS
(
SELECT PlayerID, SUM(KT.[RANK] * ISNULL(cw.[Weight], 0.10)) AS Rnk
FROM Data c
INNER JOIN FREETEXTTABLE(dbo.Data, Content, 'Karl Kognition C404') AS KT ON c.DataID = KT.[KEY]
LEFT JOIN #Weight cw ON c.DetailID = cw.DetailID
GROUP BY c.PlayerID
)
SELECT rc.Rnk,
...
I'm using a temporary table here for evidence of concept. I am considering adding a column Weights to the table Details to avoid an unnecessary table and left join.

Last Date Attended from 2 Tables SQL Server

I have searched the forum, and couldn't find an answer. So I apologize if this is out there. This seems simple in my mind, however, I can't seem to get the correct code.
I have 2 tables. STUDENT_TERMS_VIEW table holds STTR_STUDENT, STTR_TERM and TERMS table, holds the TERM_END_DATE. I need to find a way to select the student's last term based on MAX(TERM_END_DATE), but I get STTR_TERM duplicate rows per student. I need to get 1 row per student and their last term attended.
EDIT: Ok so both tables are linked by TERM.
View Code Here
As you can see, I am getting duplicate TERMS for the same student, even though I am pulling MAX(TERM_END_DATE)
SELECT * FROM
(SELECT STUDENT_TERMS_VIEW.STTR_STUDENT,
STUDENT_TERMS_VIEW.STTR_TERM,
TERMS.TERM_END_DATE
FROM STUDENT_TERMS_VIEW
JOIN STUDENT_TERMS_VIEW ON TERMS.TERMS_ID = STUDENT_TERMS_VIEW.STTR_TERM
ORDER BY TERMS.TERM_END_DATE DESC,STUDENT_TERMS_VIEW.STTR_STUDENT)
GROUP BY STUDENT_TERMS_VIEW.STTR_STUDENT
Your query is getting the max of the combination of (STTR_STUDENT and STTR_TERM). If you only want to get the max term of each student, you should only GROUP BY STUDENT_TERMS_VIEW.STTR_STUDENT. Try the query below.
SELECT stv.STTR_STUDENT, MAX(t.TERM_END_DATE)
FROM STUDENT_TERMS_VIEW stv
JOIN TERMS t ON t.TERMS_ID = stv.STTR_TERM
GROUP BY stv.STTR_STUDENT
If you also need to get the term, join it back to STUDENT_TERMS_VIEW and TERMS.
SELECT s.STTR_STUDENT, s.STTR_TERM, t.TERM_END_DATE
FROM (
SELECT stv.STTR_STUDENT, MAX(t.TERM_END_DATE) AS 'MaxDate'
FROM STUDENT_TERMS_VIEW stv
JOIN TERMS t ON t.TERMS_ID = stv.STTR_TERM
GROUP BY stv.STTR_STUDENT
) a
JOIN STUDENT_TERMS_VIEW s ON s.STTR_STUDENT = a.STTR_STUDENT
JOIN TERMS t ON t.TERMS_ID = s.STTR_TERM AND t.TERM_END_DATE = a.TERM_END_DATE

Custom use of count function in ms access

I have two tables Patient_tbl and Consult_tbl in MS access (with the fields shown below). The first one is used to record patient info, and the second one (Consult_tbl) is used to record patient visits. They are related in a one-to-many relationship using a Patient_id field.
What I need to do is to count the visitors based on gender for a given period of time, based on the consult table using Patient_id. I don’t know how to do it. Would you please help?
Patient_tbl has the following fields:
{Patient_id
P_add
P_tel
P_gender
Other fields}
Consult_tbl has the following fields:
{Consult_id
Patient_id
C_date
C_ref
Other fields}
Join the tables on Patient_id, add a where clause for the date range and use the count() function and group by P_gender:
select p.P_gender, count(*) as "count"
from Patient_tbl p
inner join Consult_tbl c on p.Patient_id = c.Patient_id
where c.C_date >= '2015-01-10'
and c.C_date <= '2015-01-20'
group by p.P_gender

Selecting multiple rows of many to many related columns into one column in SQL SERVER?

I have the following DB Tables with SQL Server
Booking(bookingID,customerName,branchID,RefNumber,...)
Trip(TripID,vehicleID,...)
BookingToTripMap(TripID,bookingID)
Branch(branchID, branchName)
Vehicle(vehicleID,vehicleNumber)
There is a one to one relationship between (Booking,Branch) and (Trip, Vehicle) and Many to many relationship between Booking, Trip which is saved in the table BookingToTripMap.
Now I want to extract a query that would return the following
Booking.RefNumber Booking.CustomerName Vehicle.VehicleNumber
(All vehicle numbers in one cell)
Here is your query
SELECT B.RefNumber, B.CustomerName, V.VehicleNumber
FROM ((Booking AS B INNER JOIN BookingToTripMap AS BT
ON B.bookingID = BT.bookingID) INNER JOIN TRIP as T
ON T.TripID = BT.TripID) INNER JOIN Vehicle as V
ON V.vehicleID = T.vehicleID
I would add the field bookingID to the table Trip, it seems that the table BookingToTripMap doesn't add any value to your database.
Also, if your vehicle's numbers are unique, you could change the primary key in the Vehicle table to vehicleNumber, and change the same columns in the Trip table. Thus you could retrieve the vehicleNumber directly from the Trip table.
I'm just guessing in that, based on the given information.
Regards,

How to fetch an object graph at once?

I'm reading a book, where the author talks about fetching an row + all linked parent rows in one step. Like fetching an order + all it's items all at once. Okay, sounds nice, but really: I've never seen an possibility in SQL to ask for - lets say - one order + 100 items? How would this record set look like? Would I get 101 rows with merged fields of both the order and the item table, where 100 rows have a lot of NULL values for the order fields, while one row has a lot of NULL values for the item fields? Is that the way to go? Or is there something much cooler? I mean... I never heard of fetching arrays onto a field?
A simple JOIN would do the trick:
SELECT o.*
, i.*
FROM orders o
INNER JOIN order_items i
ON o.id = i.order_id
The will return one row for each row in order_items. The returned rows consist of all fields from the orders table, and concatenated to that, all fields from the order_items table (quite literally, the records from the tables are joined, that is, they are combined by record concatenation)
So if orders has (id, order_date, customer_id) and order_items has (order_id, product_id, price) the result of the statement above will consist of records with (id, order_date, customer_id, order_id, product_id, price)
One thing you need to be aware of is that this approach breaks down whenever there are two distinct 'detail' tables for one 'master'. Let me explain.
In the orders/order_items example, orders is the master and order_items is the detail: each row in order_items belongs to, or is dependent on exactly one row in orders. The reverse is not true: one row in the orders table can have zero or more related rows in the order_items table. The join condition
ON o.id = i.order_id
ensures that only related rows are combined and returned (leaving out the condition would retturn all possible combinations of rows from the two tables, assuming the database would allow you to omit the join condition)
Now, suppose you have one master with two details, for example, customers as master and customer_orders as detail1 and customer_phone_numbers. Suppose you want to retrieve a particular customer along with all is orders and all its phone numbers. You might be tempted to write:
SELECT c.*, o.*, p.*
FROM customers c
INNER JOIN customer_orders o
ON c.id = o.customer_id
INNER JOIN customer_phone_numbers p
ON c.id = p.customer_id
This is valid SQL, and it will execute (asuming the tables and column names are in place)
But the problem is, is that it will give you a rubbish result. Assuming you have on customer with two orders (1,2) and two phone numbers (A, B) you get these records:
customer-data | order 1 | phone A
customer-data | order 2 | phone A
customer-data | order 1 | phone B
customer-data | order 2 | phone B
This is rubbish, as it suggests there is some relationship between order 1 and phone numbers A and B and order 2 and phone numbers A and B.
What's worse is that these results can completely explode in numbers of records, much to the detriment of database performance.
So, JOIN is excellent to "flatten" a hierarchy of items of known depth (customer -> orders -> order_items) into one big table which only duplicates the master items for each detail item. But it is awful to extract a true graph of related items. This is a direct consequence of the way SQL is designed - it can only output normalized tables without repeating groups. This is way object relational mappers exist, to allow object definitions that can have multiple dependent collections of subordinate objects to be stored and retrieved from a relational database without losing your sanity as a programmer.
This is normally done through a JOIN clause. This will not result in many NULL values, but many repeated values for the parent row.
Another option, if your database and programming language support it, it to return both result sets in one connection - one select for the parent row another for the related rows.

Resources