SQL query based on list from another query - sql-server

I am trying to build a query that will generate a list of records based on the results of a very similar query.
Here are the details and examples
Query 1: Generate a list if part #'s in a specific location of the warehouse.
Query 2: Use the list of part #'s generated in #1 to show all locations for the list of part #'s, assuming they will be in both the location specified in #1 and other locations.
Query 1 looks like this:
Select
ItemMaster.ItemNo, BinInfo.BIN, ItemDetail.Qty, ItemDetail.Whouse_ID
From
((ItemDetail
Left Join
ItemMaster on ItemMaster.ID=ItemDetail.Item_ID)
Left Join
BinInfo on BinInfo.ID = ItemDetail.Bin_ID)
Where
ItemDetail.Whouse_ID = '1'
And BinInfo.Bin = 'VLM';
Query 2 needs to be almost identical except the ItemMaster.ItemNo list will come from query #1.
Any help here would be great. I don't know if I need to learn Unions, Nested Queries, or what.

make sure that your first query returns the list of ids that you need.
then write the second query with the WHERE id IN (...) syntax:
SELECT * FROM table1 WHERE id IN
(SELECT id FROM table2 WHERE...) -- first query

Related

How can I have can I divide a single query (Cypher) in three distinct queries (Select part, Insert part And Update part)

I mean, instead of having a single query for a given input would give me a result, I would have three queries. (e.g query(input) = result)
The first one is for choosing the result, select(input) = result
The second one is for creating some elements from the result, create(result) = creation
And the third one, is for updates on the "input", update(result) = updatedResult
My query in cypher look something like that
------Select Part-------
Match (a:Node)-[*..]-(), (b:Node), (c:Node)
Where a.name = 'John', (c)-[*]-(b) ....
-------End of Select Part------------
------Create part--------------
Create (n:Town {name:a.name+'Town'})-[:isConnected]->(....)
-------End of create part-------------
------Update part-----------
Set a.name = b.name etc....
----End of update
Return a, b, c
As this query, I would be able to divide each part in their own query, not running them all at once. How could I do that?
Thank you!
I want three queries that when processed in serie would have the same effect as the query above.
I want a query that makes the select part, the with the result from that select, those results will be updated with the update query, and the create query will also use the result from the select part in order to create some items (nodes and relationships).
It's not a problem about web application, control at distance, but a programming problem, how can I have three queries when excecuted sequentially (select, create, update) is equivalent to the query above.
Thanks

Hierarchical SQL select-query

I'm using MS SqlServer 2008. And I have a table 'Users'. This table has the key field ID of bigint. And also a field Parents of varchar which encodes all chain of user's parent IDs.
For example:
User table:
ID | Parents
1 | null
2 | ..
3 | ..
4 | 3,2,1
Here user 1 has no parents and user 4 has a chain of parents 3->2->1. I created a function which parses the user's Parents field and returns result table with user IDs of bigint.
Now I need a query which will select and join IDs of some requested users and theirs parents (order of users and theirs parents is not important). I'm not an SQL expert so all I could come up with is the following:
WITH CTE AS(
SELECT
ID,
Parents
FROM
[Users]
WHERE
(
[Users].Name = 'John'
)
UNION ALL
SELECT
[Users].Id,
[Users].Parents
FROM [Users], CTE
WHERE
(
[Users].ID in (SELECT * FROM GetUserParents(CTE.ID, CTE.Parents) )
))
SELECT * FROM CTE
And basically it works. But performance of this query is very poor. I believe WHERE .. IN .. expression here is a bottle neck. As I understand - instead of just joining the first subquery of CTE (ID's of found users) with results of GetUserParents (ID's of user parents) it has to enumerate all users in the Users table and check whether the each of them is a part of the function's result (and judging on execution plan - Sql Server does distinct order of the result to improve performance of WHERE .. IN .. statement - which is logical by itself but in general is not required for my goal. But this distinct order takes 70% of execution time of the query). So I wonder how this query could be improved or perhaps somebody could suggest some another approach to solve this problem at all?
Thanks for any help!
The recursive query in the question looks redundant since you already form the list of IDs needed in GetUserParents. Maybe change this into SELECT from Users and GetUserParents() with WHERE/JOIN.
select Users.*
from Users join
(select ParentId
from (SELECT * FROM Users where Users.Name='John') as U
cross apply [GetDocumentParents](U.ID, U.Family, U.Parents))
as gup
on Users.ID = gup.ParentId
Since GetDocumentParents expects scalars and select... where produces a table, we need to apply the function to each row of the table (even if we "know" there's only one). That's what apply does.
I used indents to emphasize the conceptual parts of the query. (select...) as gup is the entity Users is join'd with; (select...) as U cross apply fn() is the argument to FROM.
The key knowledge to understanding this query is to know how the cross apply works:
it's a part of a FROM clause (quite unexpectedly; so the syntax is at FROM (Transact-SQL))
it transforms the table expression left of it, and the result becomes the argument for the FROM (i emphasized this with indent)
The transformation is: for each row, it
runs a table expression right of it (in this case, a call of a table-valued function), using this row
adds to the result set the columns from the row, followed by the columns from the call. (In our case, the table returned from the function has a single column named ParentId)
So, if the call returns multiple rows, the added records will be the same row from the table appended with each row from the function.
This is a cross apply so rows will only be added if the function returns anything. If this was the other flavor, outer apply, a single row would be added anyway, followed by a NULL in the function's column if it returned nothing.
This "parsing" thing violates even the 1NF. Make Parents field contain only the immediate parent (preferably, a foreign key), then an entire subtree can be retrieved with a recursive query.

NHibernate Criteria SQL Inner Join on Sub Select Same Table

I can't for the life of me figure out how to translate the following SQL query using NHibernate's Criteria API:
SELECT r.* from ContentItemVersionRecords as r
INNER JOIN (
SELECT ContentItemId as CID, Max(Number) as [Version]
FROM ContentItemVersionRecords
GROUP BY ContentItemId
) AS l
ON r.ContentItemId = l.CID and r.Number = l.[Version]
WHERE Latest = 0 and Published = 0
The table looks like this:
The result of the SQL query above will return the highlighted records.
The idea is to select the latest version of content items, so I basically need to group by ContentItemId and get the record with the highest Number.
So the result will look like this:
I started out with a detached criteria, but I am clueless as to how to use it in the criteria:
// Sub select for the inner join:
var innerJoin = DetachedCriteria.For<ContentItemVersionRecord>()
.SetProjection(Projections.ProjectionList()
.Add(Projections.GroupProperty("ContentItemId"), "CID")
.Add(Projections.Max("Number"), "Version"));
// What next?
var criteria = session.CreateCriteria<ContentItemVersionRecord>();
Please note that I have to use the Criteria API - I can't use LINQ, HQL or SQL.
Is this at all possible with the Criteria API?
UPDATE: I just came across this post which looks very similar to my question. However, when I apply that as follows:
var criteria = session
.CreateCriteria<ContentItemVersionRecord>()
.SetProjection(
Projections.ProjectionList()
.Add(Projections.GroupProperty("ContentItemId"))
.Add(Projections.Max("Number")))
.SetResultTransformer(Transformers.AliasToBean<ContentItemVersionRecord>());
I get 2 results, which looks promising, but all of the integer properties are 0:
UPDATE 2: I found out that if I supply aliases, it will work (meaning I will get a list of ContentItemVersionRecords with populated objects):
var criteria = session
.CreateCriteria<ContentItemVersionRecord>()
.SetProjection(
Projections.ProjectionList()
.Add(Projections.Max("Id"), "Id")
.Add(Projections.GroupProperty("ContentItemId"), "ContentItemId")
.Add(Projections.Max("Number"), "Number"))
.SetResultTransformer(Transformers.AliasToBean<ContentItemVersionRecord>());
However, I can't use the projected values as the end result - I need to use these results as some sort of input into the outer query, e.g.
SELECT * FROM ContentItemVersionRecord WHERE Id IN ('list of record ids as a result from the projection / subquery / inner join')
But that won't work, since the projection returns 3 scalar values (Id, ContentItemId and Number). If it would just return "Id", then it might work. But I need the other two projections to group by ContentItemId and order by Max("Number").
OK, so in a nutshell, you need to unwind that nested query, and do a group by with a having clause, which is pretty much a where on aggregated values, as in the following HQL:
SELECT civ.ContentItem.Id, MAX(civ.Number) AS VersionNumber
FROM ContentItemVersionRecord civ
JOIN ContentItem ci
GROUP BY civ.ContentItem.Id " +
HAVING MAX(civ.Latest) = 0 AND MAX(civ.Published) = 0
This gives you, for each deleted content items (those have all their latest and published flags to zero on all their content item version records), the maximum version number, i.e. the latest version of each deleted content item.

Transact SQL and Where Statement and CASE Statement

I have the following requirement that will multiple joins - Currently this search looks at the client’s residence county. This needs to be changed to look at tbl_client_insurance.region_id for the active, effective insurance for the consumer. If the client has multiple insurances meeting this criteria, use the county for ins_id = 2 (Medicaid). I believe I have most of the query correct, but I am getting hung up on the ti.ins_id, which I believe I will need a case, basically returning only the 2 or else if 2 does not exist return the insurance the client does have.
SELECT
ti.client_id, ti.exp_dt, ins_id, *
FROM
tbl_Client AS tc
INNER JOIN
tbl_client_insurance AS ti ON ti.client_ID = tc.client_ID
WHERE
tc.client_id = 26
AND tc.active = 1
AND (ti.exp_dt >= GETDATE() OR ti.exp_dt IS NULL)
CASE
--- Need some help here.
You may be able to add such a condition to your join logic as
INNER JOIN tbl_client_insurance AS ti ON ti.client_ID = tc.client_ID AND ti.ins_id=2
I don't know what your desired result set is.
Based on your sample you want all matching client insurances.
But it seems you may want just one for that client.
If you just want one for that client, this is just an ordering issue.
You should use a UNION ALL.
The first part is the query for insurance id 2.
The second part is the query for all other insurances.
Order by a new column you provide.
For the first part the new column will be A.
For the second part it will be B.
Then take only the top 1 row.
That way if id 2 exists it will be first, otherwise it will be one of the other insurances, or if there are no matching insurances you will get an empty result set.
The question then becomes how do you tell which of the other insurances to select if there is no medicaid. Again this is just ordering.

How do I assign weights to different columns in a full text search?

In my full text search query, I want to assign particular columns a higher weightage. Consider this query:
SELECT Key_Table.RANK, FT_Table.* FROM Restaurants AS FT_Table
INNER JOIN FREETEXTTABLE(Restaurants, *, 'chilly chicken') AS Key_Table
ON FT_Table.RestaurantID = Key_Table.[KEY]
ORDER BY Key_Table.RANK DESC
Now, I want the Name column to have a higher weightage in the results (Name, Keywords and Location are full-text indexed). Currently, if the result is found in any of the three columns, the ranks are not affected.
For example, I'd like a row with Name "Chilly Chicken" to have higher rank than one with Keywords "Chilly Chicken", but another name.
Edit:
I'm not eager to use ContainsTable, because that would mean separating the phrases (Chilly AND Chicken, etc.), which would involve me having to search all possible combinations - Chilly AND Chicken, Chilly OR Chicken, etc. I would like the FTS engine to automatically figure out which results match best, and I think FREETEXT does a fine job this way.
Apologies if I've misunderstood how CONTAINS/CONTAINSTABLE works.
The best solution is to use ContainsTable. Use a union to create a query that searches all 3 columns and adds an integer used to indicate which column was searched. Sort the results by that integer and then rank desc.
The rank is internal to sql server and not something you can adjust.
You could also manipulate the returned rank by dividing the rank by the integer (Name would be divided by 1, Keyword and Location by 2 or higher). That would cause the appearance of different rankings.
Here's some example sql:
--Recommend using start change tracking and start background updateindex (see books online)
SELECT 1 AS ColumnLocation, Key_Table.Rank, FT_Table.* FROM Restaurants AS FT_Table
INNER JOIN ContainsTable(Restaurant, Name, 'chilly chicken') AS Key_Table ON
FT_Table.RestaurantId = Key_Table.[Key]
UNION SELECT 2 AS ColumnLocation, Key_Table.Rank, FT_Table.* FROM Restaurants AS FT_Table
INNER JOIN ContainsTable(Restaurant, Keywords, 'chilly chicken') AS Key_Table ON
FT_Table.RestaurantId = Key_Table.[Key]
UNION SELECT 3 AS ColumnLocation, Key_Table.Rank, FT_Table.* FROM Restaurants AS FT_Table
INNER JOIN ContainsTable(Restaurant, Location, 'chilly chicken') AS Key_Table ON
FT_Table.RestaurantId = Key_Table.[Key]
ORDER BY ColumnLocation, Rank DESC
In a production environment, I would insert the output of the query into a table variable to perform any additional manipulation before returning the results (may not be necessary in this case). Also, avoid using *, just list the columns you really need.
Edit: You're right about using ContainsTable, you would have to modify the keywords to be '"chilly*" AND "chicken*"', I do this using a process that tokenizes an input phrase. If you don't want to do that, just replace every instance of ContainsTable above with FreeTextTable, the query will still work the same.

Resources