Problems figuring out a relational division query - sql

I've run into a problem of some complexity for my small brains to grasp. I have 4 tables, a user table that contains a user id ,a table report that contains the user id and the item id ,a table item that contains the id and a coordinate(lon,lat) and a table location that contains a bunch of coordinates(lon, lat).
I want to know which users reported an item on ALL of the locations above a certain latitude in the year X.
I know that it will involve a relational division of some sorts but I can't write the query for it.
What I'm thinking is that I want to select the users that don't fit the following criteria: From all the locations above a certain latitude, take away those that were reported by a specific user. if that returns empty, that means that the user has reported an item on ALL the locations above that latitude. Now, can you give me some help with the query part?
EXAMPLE:
USER_TABLE
user_email
tony#gmail.com
john#gmail.com
geo#gmail.com
REPORT_TABLE
user_email | item_id
tony#gmail.com | 1
geo#gmail.com | 2
geo#gmail.com | 3
ITEM TABLE
item_id | lat
1 | 10
2 | 15
3 | 20
LOCATIONS
lat
5
10
15
20
In this case, if I want ALL the users that have reported an item on a location with a lat value above 12, it should return geo#gmail.com because he has reported an item on lat = 15 and lat = 20 which are the lat values for all the locations that are above a latitude value of 12.
He geo had only reported 1 item, then the return should be empty

Related

Am I adding unnecessary data to a junction table?

I have a junction table, say for People and Locations
PersonLocations
PersonId | LocationId
---------------------
1 3
2 5
Now, Locations can belong to each other i.e. one location can sit inside another, which can sit inside another etc., so I have this defined by the Location table referencing itself:
Locations
LocationId | ArbitraryName | ParentLocationId
--------------------------------------------
1 a country null
2 region a 1
3 village 1 2
4 village 2 2
5 region b 1
So you can see village 1 and village 2 belong to region a, which in turn belongs to a country
Now, I want it to be known that if person 1 visited Location 3 (village 1) as shown in the first table, they also visited Location 2 and 1 - which can be inferred by the Location table self-referencing.
But what I've done is written rules (triggers) so that if an entry occurs on the PersonLocations table it automatically inserts the ParentLocationId (which recursively works until ParentLocationId is null)
so inserting
PersonId | LocationId
---------------------
1 3
actually results in
PersonId | LocationId
---------------------
1 3
1 2
1 1
And vice versa if I remove.
What I really want to know is - is this safe? It makes my queries and views much easier but am I missing something that is later going to bite me in the backside? I feel like as long as those triggers are in place it would be fine, although its taking up more space - the payoff justifies it? But at the same time I also feel like I might be violating some principle I don't know about...

How to retrieve all columns row count in a large table with millions of records

How to retrieve all columns row count in a large table with millions of records?
Example:
SELECT * FROM playernames;
id | name
----+------------------
38 | Abe Lincoln
39 | NULL
40 | Ronald Reagan
(3 rows)
I want to know the count of each column in one query
SELECT COUNT(ID), COUNT(NAME)
FROM PLAYERNAMEs;
result:
3 2
But as table has 10 millions of records its taking a lot of time and some times i am not getting columns count.
Is there any quicker way to retrieve this count. Thanks

Return Only Primary Keys That Share The Same Foriegn Keys [duplicate]

This question already has answers here:
Select group of rows that match all items in a list
(3 answers)
Closed 7 years ago.
We're struggling with a lightweight way to reduce results in a larger population of search results by excluding rows that don't match ALL of the foreign keys in a second table.
For example, we have a mapping table that matches documents to tags where each document can have an unlimited number of tag relationships:
DocID | TagID
12 | 1
12 | 2
34 | 1
53 | 1
53 | 4
66 | 1
66 | 2
67 | 3
We're building a table in our SPROC based on a list of tagIDs passed in by the user, so we have a second table that looks like this:
TagID
1
2
What we'd like to do is return only the rows from the first table that contain a match on every value in the second table, effectively an "and" query. So if the user passes in tag values 1 & 2 we want to return DocID 12 and 66. Right now our joins return essentially an "or" result, so values 1 & 2 would return DocIDs 12, 34, and 66.
We're currently stuck with MS SQL 2008R2.
You can do this with group by and having and a twist:
select docid
from firsttable t1 join
secondtable t2
on t1.tagid = t2.tagid
group by docid
having count(*) = (select count(*) from secondtable);
You may need count(distinct) if either table could have duplicates.

T-SQL - Getting a list of records depending on other related records values

I'm trying to make a query and need a little help (SQL Server).
Imagine the following scenario: user is viewing a web page which has several related categories. According to some rules, the page should not be displayed if a specific category has been put together with another category.
For this I've got 2 tables:
1) Has the page Id and the related categories:
Pk CategoryNumber
--------------------
1 30
1 31
1 45
2 30
3 21
3 26
3 64
4 25
4 12
5 25
5 31
5 30
5 45
2) Rules table. First row means: when viewing a page with the category 30 it should not be retrieved if it also has the 45 category.
WhenViewingCategoryNumber HideEverythingWithCategoryNumber
-------------------------------------------------------
30 45
25 31
Output expected:
2
3
4
I've spent a few hours around this and I'm not going anywhere, so I would appreciate if someone could help. If possible, would be better an answer with a SELECT statement to integrate it directly within a larger CTE statement. Many thanks.
You can use the following query to identify those page ids related to conflicting categories:
SELECT DISTINCT c1.PageId
FROM Categories AS c1
INNER JOIN Rules AS r ON c1.ItemNumber = r.WhenViewingCategoryNumber
INNER JOIN Categories AS c2 ON c1.PageId = c2.PageId
AND r.HideEverythingWithCategoryNumber = c2.ItemNumber
This will return:
PageId
------
1
5
Now you can get expected result by simply using NOT IN:
SELECT DISTINCT PageId
FROM Categories
WHERE PageId NOT IN ( ... above query here ....)
Demo here

Create duplicate records in a query for MS Access

I have the following table in a Microsoft Access Database:
TableName: Cabinets
RoomID - Number
Wall ID - Number
Cabinet ID - Number
Width - Number (double)
Height - Number (double)
Depth - Number (double)
Quantity - Number
What I need to do is create a query that will duplicate each row for a number of times specified in the Quantity field. As an example, let's say that I have the following data:
Room ID Wall ID Cabinet ID Width Height Depth Quantity
1 1 1 30 34.5 24 1
1 1 2 42 34.5 24 1
1 1 3 18 34.5 24 2
I need to have a query that would create the following:
Room ID Wall ID Cabinet ID Width Height Depth
1 1 1 30 34.5 24
1 1 2 42 34.5 24
1 1 3 18 34.5 24
1 1 3 18 34.5 24
Now, I have seen, in other questions, that I can create a 'numbers' table to accomplish this, unfortunately, I can't change the table at all. In fact, I am very limited to what I can actually do with this database.
Here is what I can do:
Create a Query that will pull the data
Create a Query that will add a 'view' to the database at runtime (before the query to pull the data is run)
Any help that can be given would be greatly appreciated. Thank you very much in advanced.
Well, this is incredibly painful in Access, but you can create a numbers table on the fly. Let's assume that cabinet_id is really a unique id in the cabinets table.
select c.*
from cabinets c left join
(select (select count(*) from cabinets c2 where c2.cabinet_id <= c.cabinet_id) as n
from cabinets c
) n
on n.n <= c.quantity;
This uses the cabinets table to generate a list of numbers, using a correlated subquery to get the numbers. Note that this assumes that quantity is always less than the number of rows in this table.
If you know the ids have no gaps and start at 1, you can simplify this to:
select c.*
from cabinets c left join
(select cabinet_id) as n
from cabinets c
) n
on n.n <= c.quantity;