Order by parent-child relation "ItemCode"-"ItemReplacementCode" in one table - sql-server

Let's suppose we have records of product with columns ItemCode and ItemReplacementCode.
Since we are talking about one table:
ItemCode is not a primary key
ItemReplacementCode isn't foreign key.
They are just simply varchars columns.
What I would like to see in select result is:
id, productname, itemcode, replacementCode
99, dell ###, a1234X, null
10034, dell ###, 1233bX, a1234X
10024, dell ###, 1232X, 1233bX
95, dell ###, 999ws, null
96, sony ###, 327b, null
and so on. Please note that itemCode and replacementCode aren't alphabetically friendly for sorting.
Can you guide me how to select products so they will be grouped and ordered by replacement code (please note, not grouped by scalar).
Certainly we are talking about kind of building graph of "relatives" retrieved by one sql statement among with "orphan" products which have no replacement.
Don't hesitate to ask for clarification.
References to similar discussions are appreciated.
I'm open to add additional hibernate relations only if they will not harm performance.

I am not 100% sure of what you are looking for, but here are some options.
To just order by replacement code
SELECT id, productname, itemcode, replacementCode
FROM ItemTable
ORDER BY replacementCode
I see that you have NULL values in the replacement code. If you want to order by the Item, but use the replacement code if there is one try this
SELECT ISNULL(replacementCode ,itemcode) AS NewItemCode ,id, productname
FROM ItemTable
ORDER BY ISNULL(replacementCode ,itemcode)

Related

How to implement many-to-many-to-many database relationship?

I am building a SQLite database and am not sure how to proceed with this scenario.
I'll use a real-world example to explain what I need:
I have a list products that are sold by many stores in various states. Not every Store sells a particular Product at all, and those that do, may only sell it in one State or another. Most stores sell a product in most states, but not all.
For example, let's say I am trying to buy a vacuum cleaner in Hawaii. Joe's Hardware sells vacuums in 18 states, but not in Hawaii. Walmart sells vacuums in Hawaii, but not microwaves. Burger King does not sell vacuums at all, but will give me a Whopper anywhere in the US.
So if I am in Hawaii and search for a vacuum, I should only get Walmart as a result. While other stores may sell vacuums, and may sell in Hawaii, they don't do both but Walmart does.
How do I efficiently create this type of relationship in a relational database (specifically, I am currently using SQLite, but need to be able to convert to MySQL in the future).
Obviously, I would need tables for Product, Store, and State, but I am at a loss on how to create and query the appropriate join tables...
If I, for example, query a certain Product, how would I determine which Store would sell it in a particular State, keeping in mind that Walmart may not sell vacuums in Hawaii, but they do sell tea there?
I understand the basics of 1:1, 1:n, and M:n relationships in RD, but I am not sure how to handle this complexity where there is a many-to-many-to-many situation.
If you could show some SQL statements (or DDL) that demonstrates this, I would be very grateful. Thank you!
An accepted and common way is the utilisation of a table that has a column for referencing the product and another for the store. There's many names for such a table reference table, associative table mapping table to name some.
You want these to be efficient so therefore try to reference by a number which of course has to uniquely identify what it is referencing. With SQLite by default a table has a special column, normally hidden, that is such a unique number. It's the rowid and is typically the most efficient way of accessing rows as SQLite has been designed this common usage in mind.
SQLite allows you to create a column per table that is an alias of the rowid you simple provide the column followed by INTEGER PRIMARY KEY and typically you'd name the column id.
So utilising these the reference table would have a column for the product's id and another for the store's id catering for every combination of product/store.
As an example three tables are created (stores products and a reference/mapping table) the former being populated using :-
CREATE TABLE IF NOT EXISTS _products(id INTEGER PRIMARY KEY, productname TEXT, productcost REAL);
CREATE TABLE IF NOT EXISTS _stores (id INTEGER PRIMARY KEY, storename TEXT);
CREATE TABLE IF NOT EXISTS _product_store_relationships (storereference INTEGER, productreference INTEGER);
INSERT INTO _products (productname,productcost) VALUES
('thingummy',25.30),
('Sky Hook',56.90),
('Tartan Paint',100.34),
('Spirit Level Bubbles - Large', 10.43),
('Spirit Level bubbles - Small',7.77)
;
INSERT INTO _stores (storename) VALUES
('Acme'),
('Shops-R-Them'),
('Harrods'),
('X-Mart')
;
The resultant tables being :-
_product_store_relationships would be empty
Placing products into stores (for example) could be done using :-
-- Build some relationships/references/mappings
INSERT INTO _product_store_relationships VALUES
(2,2), -- Sky Hooks are in Shops-R-Them
(2,4), -- Sky Hooks in x-Mart
(1,3), -- thingummys in Harrods
(1,1), -- and Acme
(1,2), -- and Shops-R-Them
(4,4), -- Spirit Level Bubbles Large in X-Mart
(5,4), -- Spiirit Level Bubble Small in X-Mart
(3,3) -- Tartn paint in Harrods
;
The _product_store_relationships would then be :-
A query such as the following would list the products in stores sorted by store and then product :-
SELECT storename, productname, productcost FROM _stores
JOIN _product_store_relationships ON _stores.id = storereference
JOIN _products ON _product_store_relationships.productreference = _products.id
ORDER BY storename, productname
;
The resultant output being :-
This query will only list stores that have a product name that contains an s or S (as like is typically case sensitive) the output being sorted according to productcost in ASCending order, then storename, then productname:-
SELECT storename, productname, productcost FROM _stores
JOIN _product_store_relationships ON _stores.id = storereference
JOIN _products ON _product_store_relationships.productreference = _products.id
WHERE productname LIKE '%s%'
ORDER BY productcost,storename, productname
;
Output :-
Expanding the above to consider states.
2 new tables states and store_state_reference
Although no real need for a reference table (a store would only be in one state unless you consider a chain of stores to be a store, in which case this would also cope)
The SQL could be :-
CREATE TABLE IF NOT EXISTS _states (id INTEGER PRIMARY KEY, statename TEXT);
INSERT INTO _states (statename) VALUES
('Texas'),
('Ohio'),
('Alabama'),
('Queensland'),
('New South Wales')
;
CREATE TABLE IF NOT EXISTS _store_state_references (storereference, statereference);
INSERT INTO _store_state_references VALUES
(1,1),
(2,5),
(3,1),
(4,3)
;
If the following query were run :-
SELECT storename,productname,productcost,statename
FROM _stores
JOIN _store_state_references ON _stores.id = _store_state_references.storereference
JOIN _states ON _store_state_references.statereference =_states.id
JOIN _product_store_relationships ON _stores.id = _product_store_relationships.storereference
JOIN _products ON _product_store_relationships.productreference = _products.id
WHERE statename = 'Texas' AND productname = 'Sky Hook'
;
The output would be :-
Without the WHERE clause :-
make Stores-R-Them have a presence in all states :-
The following would make Stores-R-Them have a presence in all states :-
INSERT INTO _store_state_references VALUES
(2,1),(2,2),(2,3),(2,4)
;
Now the Sky Hook's in Texas results in :-
Note This just covers the basics of the topic.
You will need to create combine mapping table of product, states and stores as tbl_product_states_stores which will store mapping of products, state and store. The columns will be id, product_id, state_id, stores_id.

Query to show which games which friends don't have

I have three tables set up in Access. I want to make a query that shows which games someone doesn't have in common with me.
I tried using an unmatched query, but that didn't work since each person has at least one game in common with me.
I guess I'm unsure how to handle this. The GameTimePlayed table basically has the opposite of the information I want to query, so is it possible to query that and add a "Not" conditional to "GameName" or something?
This is for a final project for class, and isn't due for about another month. I don't expect anyone to answer this for me, but even just a point in the right direction would be greatly appreciated. Everything I've tried to find so far is basically about unmatched queries, which did not work for me.
--EDIT TO PROVIDE MORE INFO--
I have all of the games in FavoriteGames. However, not all of my friends (PersonID) have all of my FavoriteGames. I'd like a query to show a record of FirstName, LastName, GameName, for each PersonID, for each GameName that he/she does not have.
Expected Behavior Example: PersonID 10 only has one GameName in common with me. The query should return five records for PersonID 10
(every game except Rocket League).
Sample Data:
tbl_FavoriteGames
tbl_FriendsWithGame
tbl_GameTimePlayed
GameName is the Primary Key for tbl_FavoriteGames
PersonID is the Primary Key for tbl_FriendsWithGame
PersonID, GameName Foreign Keys form a Composite Primary Key for tbl_GameTimePlayed
This is the closest I have gotten so far (still way off though) in that it removes the specified GameName:
SELECT *
FROM tbl_GameTimePlayed
WHERE NOT EXISTS
(
SELECT *
FROM tbl_FriendsWithGame
WHERE tbl_GameTimePlayed.PersonID = tbl_FriendsWithGame.PersonID
AND tbl_GameTimePlayed.GameName = tbl_FavoriteGames.GameName
);
It prompts me to enter a GameName (no idea why). When I enter a GameName, it returns all records that don't have that specific GameName.
This returns 6 games for each person, whether or not the person actually has that game. Could be useful since it contains the people/games that aren't in common.
SELECT PersonID, GameName
FROM tbl_FriendsWithGame, tbl_FavoriteGames
WHERE EXISTS (SELECT PersonID FROM tbl_GameTimePlayed WHERE GameName = tbl_GameTimePlayed.GameName);
I tried "WHERE NOT EXISTS" and that returned 0 results.
--SECOND EDIT: SOLVED!!--
I took a fresh look at the problem today, and figured it out! I used the code mentioned above to query (qry_AllPeopleAllGames) a list of all of the games, for all of the people (so 6 entries per person):
SELECT PersonID, GameName
FROM tbl_FriendsWithGame, tbl_FavoriteGames
WHERE EXISTS (SELECT PersonID FROM tbl_GameTimePlayed WHERE GameName = tbl_GameTimePlayed.GameName);
Then, I made another query that compared the qry_AllPeopleAllGames list to my tbl_GameTimePlayed (which is the list of people, games they actually own, and hours played) and spit out a list of FirstName & LastInitial and GameName that don't exist in the real list:
SELECT [tbl_FriendsWithGame]![FirstName] & " " & [tbl_FriendsWithGame]![LastInitial] AS FullName, GameName
FROM qry_AllPeopleAllGames INNER JOIN tbl_FriendsWithGame ON qry_AllPeopleAllGames.PersonID = tbl_FriendsWithGame.PersonID
WHERE ((NOT Exists (SELECT PersonID, GameName
FROM tbl_GameTimePlayed
WHERE qry_AllPeopleAllGames.PersonID = tbl_GameTimePlayed.PersonID AND qry_AllPeopleAllGames.GameName = tbl_GameTimePlayed.GameName
)));
****NOTE:**** The first part of the SELECT is not needed, I just used it for easier viewing in my actual query results (showing first name/last initial in one field).
I'm really excited that I figured this out! I'm sure there are better/more efficient ways to do this, and if you want to share, please let me know!
I included this in my initial post, but I'll post this as the answer as well.
I took a fresh look at the problem today, and figured it out! Last night while trying to test random possible solutions, I accidently made a query that lists all of the games, for all of the people (so 6 entries per person). Today, I used it as part of the solution, qry_AllPeopleAllGames:
SELECT PersonID, GameName
FROM tbl_FriendsWithGame, tbl_FavoriteGames
WHERE EXISTS (SELECT PersonID FROM tbl_GameTimePlayed WHERE GameName = tbl_GameTimePlayed.GameName);
Then, I made another query that compared the qry_AllPeopleAllGames list to my tbl_GameTimePlayed, which is the real list of people/games/hours played.
It returns the FirstName&LastInitial and the GameName for each PersonID/GameName combo that doesn't appear in the tbl_GameTimePlayed table. Here is the code:
SELECT [tbl_FriendsWithGame]![FirstName] & " " & [tbl_FriendsWithGame]![LastInitial] AS FullName, GameName
FROM qry_AllPeopleAllGames INNER JOIN tbl_FriendsWithGame ON qry_AllPeopleAllGames.PersonID = tbl_FriendsWithGame.PersonID
WHERE ((NOT Exists (SELECT PersonID, GameName
FROM tbl_GameTimePlayed
WHERE qry_AllPeopleAllGames.PersonID = tbl_GameTimePlayed.PersonID AND qry_AllPeopleAllGames.GameName = tbl_GameTimePlayed.GameName
)));
NOTE: The first part of the SELECT is not needed, I just used it for easier viewing in my actual query results (showing first name/last initial in one field).
I'm really excited that I figured this out! I'm sure there are better/more efficient ways to do this, and if you want to share, please let me know!
You need a dataset of all possible pairs of friends/games in order to determine which games each friend does not have. Do you have a tbl_Friends? Consider:
Query1:
SELECT tblFriends.ID, tbl_FavoriteGames.ID FROM tblFriends, tbl_FavoriteGames;
That is a Cartesian query - without JOIN clause every record of each table will associate with each record of other table.
Query2:
SELECT Query1.tblFriends.ID, Query1.tbl_FavoriteGames.ID
FROM tbl_FriendsWithGame RIGHT JOIN Query1 ON (tbl_FriendsWithGame.GameID = Query1.tbl_FavoriteGames.ID) AND (tbl_FriendsWithGame.FriendID = Query1.tblFriends.ID) WHERE tbl_FriendsWithGame.GameID IS NULL;
Or if you don't have tbl_Friends
SELECT DISTINCT tbl_FriendsWithGame.FriendID, tbl_FavoriteGames.ID
FROM tbl_FavoriteGames, tbl_FriendsWithGame;
Then adjust Query2.

SQL Server tables extend to many tables

I'm handling this problem here in company: We have different customers, that need different fields in the same table, but we do not want to have a table with 300 columns, which is inneficient, hard to use and so on. Example:
table_products have this fields: product_id, product_name, product_cost.
then, the first client 'X' needs the field product_registerid.
the client 'Y' needs the field product_zipareaid.
That happens by different causes. Example: they are from different states, that have different rules.
At this moment we came up with this solution, which i don't like:
product_id, product_name, product_cost, product_personal. In this product_personal we have saved values like '{product_registerid:001;product_zipareaid:001-000131}'.
I came up with a theoretic solution: extend the table, and the sql will know when i do a query in the extended table, and shou me the column with the main table's column. Something like:
table_products with columns product_id, product_name, product_cost.
table_products_x with column product_registerid.
table_products_y with column product_zipareaid.
And the querys would return:
1.
select * from table_products where product_registerid = 001:
product_id, product_name, product_cost, product_registerid
1, iphone, 599, 001.
2.
select * from table_products where product_zipareaid = 000-000110:
product_id, product_name, product_cost, product_zipareaid
1, iphone, 599, 000-000110.
So, im accepting different suggestions for solving our problem.
Thank you in advance!
One approach would be to add a single Extended Properties table, that would look something like this:
Product_id (FK)
Client_id
PropertyName
PropertyValue
And so it would be populated with values like:
Product_id Client_id PropertyName PropertyValue
1 x product_registerid 001
1 y product_zipareaid 000-000110
Then you just join table_products to Extended_properties on Product_Id and put the Client_id(s) you want in the WHERE clause.
Note that you'll probably end up wanting to use a PIVOT query to get multiple extended properties for each client.

SQL Search for multple words and sort results by relevance

Hope this will be simple for some of database gurus.
I’ll try to be as short and concise as possible.
I’m not too good with SQL I need a query (using SQL Server 2008) that will do the following:
Let’s say there is a Products table with columns like this (simplified):
ID, Title, Description, BrandID, TypeID, Price
Tasks:
Return all rows that matching BrandID
Return all rows that matching TypeID
Return all rows where Price is over or equal some value
Need to implement SQL paging with e.g. BETWEEN where will send parameters for: start row, end row and page size.
Results will be sometimes sorted by Price (ASC or DESC) and sometimes by Title (also ASC or DESC)
So far nothing to much complicated and I already have an solution.
For example:
SELECT T.*
FROM (
SELECT COUNT(1) OVER() AS TotalRecords,
ROW_NUMBER() OVER(ORDER BY Product.Price ASC) AS RowNumber,
Product.*
FROM Product
WHERE Products.BrandID = #BrandID
AND Products.TypeID = #TypeID
AND Products.Price >= #Price
) AS T
WHERE T.RowNumber BETWEEN #StartRowNumber AND #EndRowNumber
Now the fun starts: I need to add one more criteria to search and its by keywords that will be multiple words (separated by space) and should match them against Title and Description columns and in this case Order By will be by relevance where relevance is number of matched words within Title column so the results with where title match most of words from keywords will be returned first. Description column is ignored when counting relevance, it just needs to match at least one word from keywords LIKE ‘%’ + #Word ‘%’
Of course, keywords will be split into #Words but that is not a problem.
One important note: using full text search will be most appropriate solution but in this case I can’t use it!
Any help is appreciated.
Thanks in advance!

joining latest of various usermetadata tags to user rows

I have a postgres database with a user table (userid, firstname, lastname) and a usermetadata table (userid, code, content, created datetime). I store various information about each user in the usermetadata table by code and keep a full history. so for example, a user (userid 15) has the following metadata:
15, 'QHS', '20', '2008-08-24 13:36:33.465567-04'
15, 'QHE', '8', '2008-08-24 12:07:08.660519-04'
15, 'QHS', '21', '2008-08-24 09:44:44.39354-04'
15, 'QHE', '10', '2008-08-24 08:47:57.672058-04'
I need to fetch a list of all my users and the most recent value of each of various usermetadata codes. I did this programmatically and it was, of course godawful slow. The best I could figure out to do it in SQL was to join sub-selects, which were also slow and I had to do one for each code.
This is actually not that hard to do in PostgreSQL because it has the "DISTINCT ON" clause in its SELECT syntax (DISTINCT ON isn't standard SQL).
SELECT DISTINCT ON (code) code, content, createtime
FROM metatable
WHERE userid = 15
ORDER BY code, createtime DESC;
That will limit the returned results to the first result per unique code, and if you sort the results by the create time descending, you'll get the newest of each.
I suppose you're not willing to modify your schema, so I'm afraid my answe might not be of much help, but here goes...
One possible solution would be to have the time field empty until it was replaced by a newer value, when you insert the 'deprecation date' instead. Another way is to expand the table with an 'active' column, but that would introduce some redundancy.
The classic solution would be to have both 'Valid-From' and 'Valid-To' fields where the 'Valid-To' fields are blank until some other entry becomes valid. This can be handled easily by using triggers or similar. Using constraints to make sure there is only one item of each type that is valid will ensure data integrity.
Common to these is that there is a single way of determining the set of current fields. You'd simply select all entries with the active user and a NULL 'Valid-To' or 'deprecation date' or a true 'active'.
You might be interested in taking a look at the Wikipedia entry on temporal databases and the article A consensus glossary of temporal database concepts.
A subselect is the standard way of doing this sort of thing. You just need a Unique Constraint on UserId, Code, and Date - and then you can run the following:
SELECT *
FROM Table
JOIN (
SELECT UserId, Code, MAX(Date) as LastDate
FROM Table
GROUP BY UserId, Code
) as Latest ON
Table.UserId = Latest.UserId
AND Table.Code = Latest.Code
AND Table.Date = Latest.Date
WHERE
UserId = #userId

Resources