What am I / Was I missing on this SQL request? - sql

I apologize for any vagueness, this is about a question I had on a entry level SQL position test last week. I couldn't figure out how to do this at the time, and can't seem to figure anything out now.
Basically I was provided with 3 tables. One was Recipes (had recipe name, ID, instructions and notes), one was RecipeIngredients (had recipe ID, ingredient ID, ingredients, and quantity of ingredients), and the third was Ingredients (ingredient ID and ingredient). Something like that.
I had a few queries with JOIN statements that showed how to make certain recipes and so on. But I couldn't figure out quite how to manage the final question. The final question was something like:
"Provide 2 sets of queries at once. 1st query - Return Ingredients, quantities, and notes for a specific ID. 2nd Query - Return the instructions for the same Recipe ID. Write the queries so that the user can easily alter the recipe ID in one place only for both queries in order to query for different recipe IDs."
I know we can't alias a WHERE clause, but that is the only thing I can remotely think of for doing 2 queries at once with only specifying the WHERE once. I tried to see if I could do it with a subquery but had no luck. I considered UNION... but there were different columns and different values in each query so that's a no go.
Is there something I'm missing? Or did I just completely fail when trying to set this up as a subquery? I apologize for vagueness, it's been a few days and I've been too busy to remember to post this on here. I've found a lot of help anonymously browsing this site in the past so I figured it was worth posting this as I've not seen anything similar so far.

Using SQL Server, you could have declared a variable, like #recipeID, then used it in two queries. That would allow you to change the value in one place, and have it used in 2 queries.
DECLARE #recipeID INT = 123
SELECT *
FROM recipes
WHERE recipeID = #recipeID
SELECT *
FROM recipe_ingredients
WHERE recipeID = #recipeID

Related

SQL - Return first instance of record when many instances are found in database

I am working with some inherited code and having trouble solving the following issue: I would like to be able to search our inventory database by a number of criteria, including hardware serial number and comments left about the hardware. I then would like to return a list of pieces of hardware in our inventory that match this search criteria.
The issue I am having is that there may be multiple comments for one piece of hardware, so when returning the list of hardware, I see multiple results for the same piece (because of the joining to the Events table). How can I display the record once for each piece of hardware instead of multiple records for each piece of hardware?
Here is a snippet of the SQL query minus all of the if statements containing search parameters and such:
SELECT
UPPER(Hardware.HardwareSerialNumber) AS HardwareSerialNumber,
UPPER(Hardware.HardwareName) AS HardwareName,
Hardware.HardwareFirstDeploymentDate,
Hardware.HardwareActive,
Hardware.HardwareAccountNumber,
Hardware.BillingAccountNumber,
Hardware.LastUpdated,
Hardware.Comments,
Events.EventComments
FROM
Hardware
LEFT JOIN
Events
ON
Hardware.HardwareSerialNumber = Events.HardwareSerialNumber
WHERE 1=1
Thank you in advance!
I read your question and the comments a few times and I think your question is perhaps simpler than we are making it. It sounds like you have the data you WANT but just can't figure out how to tease out subsets from it. I would suggest that the following approaches are possible.
GROUP
This is a solution on the client side Since you have the data you want, order by serial number then output based on the GROUP param of cfoutput. Something like:
<cfoutput group="serialnumber">
.... output a line item for the hardware.
<cfoutput group="comments">
... line item for each comment.
</cfoutput>
</cfoutput>
Of course if you just want to show the hardware piece itself, just do the OUTER cfoutput and skip the inner one.
SELECT DISTINCT Q OF A Q
Again this is a client side solution. Since you have the data you want run a separate query of a query and select distinct the rows that are related to the hardware (sans comments).
<cfquery name="hardware" dbtype="query">
SELECT DISTINCT hardwareserialnumber, hardwarename
FROM qryHardware
</cfquery>
...where qryHardware in the example is the name of the query returned above. Then use this subquery as you need.
Split Queries
You might try just running 2 queries, one containing the hardware and the other the comments. If you need to search against the comments use a subquery to figure out WHICH hardware serials you should be working with, then pull in the comments in a second query. Often people work really really hard to get all the data into one query - which is a laudible goal and lets the DB do what it does best, but there are times when it is a bit of a wasted effort. Lineheader / lineitem or Item / comment can be one of those times IMO.

Query to Find Adjacent Date Records

There exists in my database a page_history table; the idea is that whenever a record in the page table is changed, that record's old values are stored in the history table.
My job now is to find occasions in which a record was changed, and retrieve the pre- and post-conditions of that change. Specifically, I want to know when a page changed groups, and what groups were involved in the change. The query I have below can find these instances, but with the use of the min function, I can only get back the values that match between the two records:
select page_id,
original_group,
min(created2) change_date
from (select h.page_id,
h.group_id original_group,
i.group_id new_group,
h.created_dttm created1,
i.created_dttm created2
from page_history h,
page_history i
where h.page_id = i.page_id
and h.created_dttm < i.created_dttm
and h.group_id != i.group_id)
group by page_id, original_group, created1
order by page_id
When I try to get, say, any details of the second record, like new_group, I'm hit with a ORA-00979: not a GROUP BY expression error. I don't want to group by new_group, though, because that's going to destroy the logic (I think it would find records displaying times a page changed from a group to another group, regardless of any changes to other groups in between).
My question, then, is how can I modify this query, or go about writing a new one, that achieves a similar end, but with the added availability of columns that do not match between the two records? In essence, how can I find that min record without sacrificing all the other columns I'm not trying to compare? I don't exactly need a complete answer, any suggestions that point me in the right direction would be appreciated.
I use PL/SQL Developer, and it looks like version 11.2.0.2.0 of Oracle.
EDIT: I have found a solution. It's not pretty, and I'd still like to see some alternatives, but if helping me out would threaten to explode your brain, I would advise relocating to an easier question.
Without seeing your table structure it's hard to re-write the query but when you have a min function used like that it invariably seems better to put it into a separate sub select to get what you want and then compare the result of that.

How to create a faceted search with SQL Server

I have an application which I will be accessing SQL server to return data which has been filtered by selections from the application as any common faceted search. I did see some out the box solutions, but these are expensive and I prefer building out something custom, but just don't know where to start.
The database structure is like this:
The data from the PRODUCT table would be searched by tags from the TAG table. Values which would be found in the TAG table would be something like this:
ID NAME
----------------------
1 Blue
2 Green
3 Small
4 Large
5 Red
They would be related to products through the ProductTag table.
I would need to return two groups of data from this setup:
The Products that are only related to the Tags selected, whether single or multiple
The Remaining tags that are also available to select for the products which have already been refined by single or multiple selected tags.
I would like this to be all with-in SQL server if possible, 2 seperate as stored procedures.
Most websites have this feature built into it these days, ie: http://www.gnc.com/family/index.jsp?categoryId=2108294&cp=3593186.3593187 (They've called it 'Narrow By')
I have been searching for a while how to do this, and I'm taking a wild guess that if a stored procedure has to be created in this nature, that there would need to be 1 param that accepts CSV values, like this:
[dbo].[GetFacetedProducts] #Tags_Selected = '1,3,5'
[dbo].[GetFacetedTags] #Tags_Selected = '1,3,5'
So with this architecture, does anyone know what types of queries need to be written for these stored procedures, or is the architecture flawed in any way? Has anyone created a faceted search before that was like this? If so, what types of queries would be needed to make something like this? I guess I'm just having trouble wrap my head around it, and there isn't much out there that shows someone how to make something like this.
A RDBMS for being used for faceted searching is the wrong tool for the job at hand. Faceted searching is a multidimensional search, which is difficult to express in the set-based SQL language. Using a data-cube or the like might give you some of the desired functionality, but would be quite a bit of work to build.
When we were faced with similar requirements we ultimately decided to utilize the Apache Solr search engine, which supports faceting as well as many other search-oriented functions and features.
It is possible to do faceted search in SQL Server. However don't try to use your live product data tables. Instead create a de-normalised "fact" table which holds every product (rows) and every tag (columns) so that the intersection is your product-tag values. You can re-populate this periodically from your main product table.
It is then straightforward and relatively efficient to get the facet counts for the matching records for each tag the user checks.
The approach I have described will be perfectly good for small cases, e.g. 1,000 product rows and 50-100 tags (attributes). Also there is an interesting opportunity with the forthcoming SQL Server 2014, which can place tables in memory - that should allow much larger fact tables.
I have also used Solr, and as STW points out this is the "correct" tool for facet searches. It is orders of magnitude faster than a SQL Server solution.
However there are some major disadvantages to using Solr. The main issue is that you have to setup not only another platform (Solr) but also all the paraphernalia that goes with it - Java and some kind of Java servlet (of which there are several). And whilst Solr runs on Windows quite nicely, you will still soon find yourself immersed in a world of command lines and editing of configuration files and environment variables that will remind you of all that was great about the 1980s ... or possibly not. And when that is all working you then need to export your product data to it, using various methods - there is a SQL Server connector which works fairly well but many prefer to post data to it as XML. And then you have to create a webservice-type process on your application to send it the user's query and parse the resulting list of matches and counts back into your application (again, XML is probably the best method).
So if your dataset is relatively small, I would stick with SQL Server. You can still get a sub-second response, and SQL 2014 will hopefully allow much bigger datasets. If your dataset is big then Solr will give remarkably fast results (it really is very fast) but be prepared to make a major investment in learning and supporting a whole new platform.
There's other places where you can get examples of turning a CSV parameter into a table variable. Assuming you have done that part your query boils down to the following:
GetFacetedProducts:
Find Product records where all tags passed in are assigned to each product.
If you wrote it by hand you could end up with:
SELECT P.*
FROM Product P
INNER JOIN ProductTag PT1 ON PT1.ProductID = P.ID AND PT1.TagID = 1
INNER JOIN ProductTag PT2 ON PT1.ProductID = P.ID AND PT1.TagID = 3
INNER JOIN ProductTag PT3 ON PT1.ProductID = P.ID AND PT1.TagID = 5
While this does select only the products that have those tags, it is not going to work with a dynamic list. In the past some people have built up the SQL and executed it dynamically, don't do that.
Instead, lets assume that the same tag can't be applied to a product twice, so we could change our question to:
Find me products where the number of tags matching (dynamic list) is equal to the number of tags in (dynamic list)
DECLARE #selectedTags TABLE (ID int)
DECLARE #tagCount int
INSERT INTO #selectedTags VALUES (1)
INSERT INTO #selectedTags VALUES (3)
INSERT INTO #selectedTags VALUES (5)
SELECT #tagCount = COUNT(*) FROM #selectedTags
SELECT
P.ID
FROM Product P
JOIN ProductTag PT
ON PT.ProductID = P.ID
JOIN #selectedTags T
ON T.ID = PT.TagID
GROUP BY
P.ID,
P.Name
HAVING COUNT(PT.TagID) = #tagCount
This returns just the ID of products that match all your tags, you could then join this back to the products table if you want more than just an ID, otherwise you're done.
As for your second query, once you have the product IDs that match, you want a list of all tags for those product IDs that aren't in your list:
SELECT DISTINCT
PT2.TagID
FROM aProductTag PT2
WHERE PT2.ProductID IN (
SELECT
P.ID
FROM aProduct P
JOIN aProductTag PT
ON PT.ProductID = P.ID
JOIN #selectedTags T
ON T.ID = PT.TagID
GROUP BY
P.ID,
P.Name
HAVING COUNT(PT.TagID) = #tagCount
)
AND PT2.TagID NOT IN (SELECT ID FROM #selectedTags)

Counting occurence of each distinct element in a table

I am writing a log viewer app in ASP.NET / C#. There is a report window, where it will be possible to check some information about the whole database. One kind of information there I want to display on the screen is the number of times each generator (an entity in my domain, not Firebirds sequence) appears in the table. How do I do that using COUNT ?
Do I have to :
Gather the key for each different generator
Run one query for each generator key using count
Display it somehow
Is there any way that I can do it without having to do two queries to the database? The database size can be HUGE, and having to query it "X" times where "X" is the number of generators would just suck.
I am using a Firebird database, is there any way to fetch this information from any metadata schema or there is no such thing available?
Basically, what I want is to count each occurrence of each generator in the table. Result would be something like : GENERATOR A:10 times,GENERATOR B:7 Times,Generator C:0 Times and so on.
If I understand your question correctly, it is a simple matter of using the GROUP BY clause, e.g.:
select
key,
count(*)
from generators
group by key;
Something like the query below should be sufficient (depending on your exact structure and requirements)
SELECT KEY, COUNT(*)
FROM YOUR_TABLE
GROUP BY KEY
I solved my problem using this simple Query:
SELECT GENERATOR_,count(*)
FROM EVENTSGENERAL GROUP BY GENERATOR_;
Thanks for those who helped me.
It took me 8 hours to come back and post the answer,because of the StackOverflow limitation to answer my own questions based in my reputation.

MySQL: Limit output according to associated ID

So here's my situation. I have a books table and authors table. An author can have many books... In my authors page view, the user (logged in) can click an author in a tabled row and be directed to a page displaying the author's books (collected like this URI format: viewauthorbooks.php?author_id=23), very straight forward... However, in my query, I need to display the books for the author only, and not all books stored in the books table (as i currently have!) As I am a complete novice, I used the most simple query of:
SELECT * FROM books
This returns the books for me, but returns every single value (book) in the database, and not ones associated with the selected author. And when I click a different author the same books are displayed for them...I think everyone gets what I'm trying to achieve, I just don't know how to perform the query. I'm guessing that I need to start using more advanced query clauses like INNER JOIN etc. Anyone care to help me out :)
Enters the WHERE clause:
The WHERE clause is used to extract only those records that fulfill a specific criteria. In your case, all you need to do is:
SELECT * FROM tasks_tb WHERE author_id = '23';
You will obviously need to change the '23' with the value passed in the URL querystring, so that each page lists the books of each relevant author.
Since it is never too early to start reading about best practices, note that for public websites it is really dangerous to include any un-sanitized input into an SQL query. You may want to read further on this topic from the following Stack Overflow posts:
XKCD sql injection - please explain (with pictures!)
What is SQL injection?
Is SQL injection a risk today?
SQL Injection Topics on Stack Overflow
The basic syntax is
SELECT * FROM table WHERE column=value;
But you really need to study more SQL, I'd suggest going through sqlzoo tutorial. http://sqlzoo.net/