Comparing data in same table SQL - sql

Table: working_history
ID Field Event_dt Data
145 Reason 10/20/2003 DOM
145 Reason 9/20/2007 LVE
145 Reason 3/17/2008 RTN
145 Reason 4/5/2008 POP
145 Reason 3/7/2009 POP
145 Reason 6/13/2009 TRE
145 status 10/20/2003 A
145 status 6/5/2006 L
145 status 11/27/2006 A
145 status 9/20/2007 L
145 status 3/17/2008 A
145 status 6/12/2009 T
I want anyone who had an ESTA=L, and then check to make sure that their respective Reason event_dt match Status event_dt. In the above table,
145 status 6/5/2006 L
should come back, as event_dt (6/5/2006) for field.status = L, does not have a data where Field = reason on the same date.

SELECT *
FROM working_History WHReason
RIGHT JOIN Working_history WHStatus
on WHReason.ID = WHStatus.ID
and WHReason.field = 'Reason'
and WHStatus.field = 'status' and WHStatus.Data='L'
and WHReason.Event_DT = WHStatus.Event_DT
WHERE WHReason.Event_Date is null
Assuming you're only looking for status's that do NOT have a reason and not the other way.
This basically says, create two sets of information one for fields of reason one for fields of status. Then combine those two results based on their ID and event date including all records from the status set and only those that match in reason. Limit that result to only include those that have no reason event date.
It uses a concept called a Self Join to generate two sets of data allowing one to quickly identify information in one set but not the other. Visual reference

Related

How to write a query to return only specified list of rows?

There is a table Shops with Shop_number and Shop Address columns.
Also a table called Properties with two columns:
Shop_number
Property_ID
222222
113
222222
114
222222
115
222223
113
222224
113
222225
111
222226
112
A shop can have more than one property.
How to write a query which would return all shop numbers which does not have Property_ID: 113 at all (excluding 222222, because it indeed has other properties, but also 113).
SELECT p.shop_number FROM Properties p
WHERE p.property_id != 113
My query returns also store 222222 which has 113 property_id.
I would like to return shop numbers: 222225 and 222226 in this case only.
Your description is a bit unclear.
Since you already got an answer how to get your result in case you have to use one of the two tables only, let's have a look on your requirements again which can be read as you need both tables.
You are writing: "A shop can have more than one property. How to write a query which would return all shop numbers which does not have Property_ID: 113 at all"
I don't know if this is your intention, but according to your description, you also want to get all shops that don't even occur at all in the properties table.
So we could use such a query:
SELECT s.shop_number
FROM shops s
WHERE NOT EXISTS
(SELECT 1 FROM properties WHERE property_id = 113
AND shop_number=s.shop_number);
This will show all shop numbers that don't appear at all in the properties table and also all shop numbers that appear having properties different from 113 only.
Only those shop_numbers that occur in the properties table and exist there having the property id 113 will be excluded.
And this is exactly what you described as being your requirement. It's the question if what you told us you want to do is really what you want to do ;)
Either use not exists as #Larnu suggests or use group by / having:
select shopnumber
from t
group by shopnumber
having count(case property_id when 113 then 1 end) = 0;
case maps property_id = 113 to 1 and everything else to null. count(x) does not count rows where x is null.

Select items where count in another field matches (not updatable)

Here I am trying to get the record for my products where the # swab location in Main table matches the count of swab locations in swab Table and Users can checked off the Y/N to verify that the description of the locations are correct.
Here is the example of my 2 tables.
tblMainEquipment
Asset_ID EquipmentName Num_SwapLocations Verified
234 Saijimon 2 N
235 Pasquale 3 N
tblMainSwapLocations
Asset_ID Swap_location
234 Particle Cannon
234 RailGun
235 Particle Cannon
I use the following query to count the number of records, i avoided using a having query to combine both tables since it is not updatable.
qryMainSwapLocationCount
SELECT MSL.Asset_ID, Count(Asset_ID) AS [Count]
FROM tblMainSwapLocation AS MSL
GROUP BY MSL.Asset_ID;
This will give me the result of
qryMainSwapLocationCount
Asset_ID count
234 2
234 1
I used the following as a record source for my form to allow users to verify the inputs.
SELECT MEQ.Asset_ID, MEQ.Equipment_Name,MEQ.Num_swapLocations MEQ.Verified
FROM tblMainEquipment AS MEQ, qryMainSwapLocationCount AS MSLC
WHERE (((MEQ.Asset_ID)=[MSLC].[Asset_ID]) AND ((MEQ.Num_SwapLocations)=[MSLC].[Count]);
This result would be
tblMainEquipment
Asset_ID EquipmentName Num_SwapLocations Verified
234 Saijimon 2 N
However this record set is not editable. Is there any reasons for this?
I think you should put your table tblMainEquipment as your recordsource and bring all the fields from that on to your form:
Then insert an unbound textbox (perhaps close to your Num_SwapLocations field for easy comparison):
Then in this new textbox, put the following in the ControlSource:
=DCount("ASSET_ID","tblMainSwapLocations","ASSET_ID=" & [Asset_ID])
Then open your form and it should count the number of records in table tblMainSwapLocations that have the same Asset_ID as the record currently showing:
You'll then be able to update the Verified field in your tblMainEquipment table.

Is it possible to match the "next" unmatched record in a SQL query where there is no strictly unique common field between tables?

Using Access 2010 and its version of SQL, I am trying to find a way to relate two tables in a query where I do not have strict, unique values in each table, using concatenated fields that are mostly unique, then matching each unmatched next record (measured by a date field or the record id) in each table.
My business receives checks that we do not cash ourselves, but rather forward to a client for processing. I am trying to build a query that will match the checks that we forward to the client with a static report that we receive from the client indicating when checks were cashed. I have no control over what the client reports back to us.
When we receive a check, we record the name of the payor, the date that we received the check, the client's account number, the amount of the check, and some other details in a table called "Checks". We add a matching field which comes as close as we can get to a unique identifier to match against the client reports (more on that in a minute).
Checks:
ID Name Acct Amt Our_Date Match
__ ____ ____ ____ _____ ______
1 Dave 1001 10.51 2/14/14 1001*10.51
2 Joe 1002 12.14 2/28/14 1002*12.14
3 Sam 1003 50.00 3/01/14 1003*50.00
4 Sam 1003 50.00 4/01/14 1003*50.00
5 Sam 1003 50.00 5/01/14 1003*50.00
The client does not report back to us the date that WE received the check, the check number, or anything else useful for making unique matches. They report the name, account number, amount, and the date of deposit. The client's report comes weekly. We take that weekly report and append the records to make a second table out of it.
Return:
ID Name Acct Amt Their_Date Unique1
__ ____ ____ ____ _____ ______
355 Dave 1001 10.51 3/25/14 1001*10.51
378 Joe 1002 12.14 4/04/14 1002*12.14
433 Sam 1003 50.00 3/08/14 1003*50.00
599 Sam 1003 50.00 5/11/14 1003*50.00
Instead of giving us back the date we received the check, we get back the date that they processed it. There is no way to make a rule to compare the two dates, because the deposit dates vary wildly. So the closest thing I can get for a unique identifier is a concatenated field of the account number and the amount.
I am trying to match the records on these two tables so that I know when the checks we forward get deposited. If I do a simple join using the two concatenated fields, it works most of the time, but we run into a problem with payors like Sam, above, who is making regular monthly payments of the same amount. In a simple join, if one of Sam's payments appears in the Return table, it matches to all of the records in the Checks table.
To limit that behavior and match the first Sam entry on the Return table to the first Sam entry on the Checks table, I wrote the following query:
SELECT return.*, checks.*
FROM return, checks
WHERE (( ( checks.id ) = (SELECT TOP 1 id
FROM checks
WHERE match = return.unique1
ORDER BY [our_date]) ));
This works when there is only one of Sam's records in the Return table. The problem comes when the second entry for Sam hits the Return table (Return.ID 599) as the client's weekly reports are added to the table. When that happens, the query appropriately (for my purposes) only lists that two of Sam's checks have been processed, but uses the "Top 1 ID" record to supply the row's details from the Return table:
Checks_Return_query:
Checks.ID Name Acct Amt Our_Date Their_Date Return.ID
__ ____ ____ ____ _____ ______ ________
1 Dave 1001 10.51 2/14/14 3/25/14 355
2 Joe 1002 12.14 2/28/14 4/04/14 378
3 Sam 1003 50.00 3/01/14 3/08/14 433
4 Sam 1003 50.00 4/01/14 3/08/14 433
In other words, the query repeats the Return table info for record Return.ID 433 instead of matching Return.ID 599, which is I guess what I should expect from the TOP 1 operator.
So I am trying to figure out how I can get the query to take the two concatenated fields in Checks and Return, compare them to find matching sets, then select the next unmatched record in Checks (with "next" being measured either by the ID or Our_Date) with the next unmatched record in Return (again, with "next" being measured either by the ID or Their_Date).
I spent many hours in a dark room turning the query into various joins, and back again, looking at functions like WHERE NOT IN, WHERE NOT EXISTS, FIRST() NEXT() MIN() MAX(). I am afraid I am way over my head.
I am beginning to think that I may have a structural problem, and may need to write the "matched" records in this query to another table of completed transactions, so that I can differentiate between "matched" and "unmatched" records better. But that still wouldn't help me if two of Sam's transactions are on the same weekly report I get from my client.
Are there any suggestions as to query functions I should look into for further research, or confirmation that I am barking up the wrong tree?
Thanks in advance.
I'd say that you really need another table of completed transactions, it could be temporary table.
Regarding your fears "... if two of Sam's transactions are on the same weekly report ", you can use cursor in order to write records "one-by-one" instead of set based transaction.

GROUP BY query text columns

I'm using a query, I want to group them by 1 specific column. i get an error for other field witch are not listed in GROUP BY clause, yes i know i should tell Sql Server witch row of grouped by rows you should pick but i don't know how to do that. If that's int i use MIN() function and if its a text field i don't know what to do.
I want to select all data and if videos.number,videos.title is replicated than get the first row of grouped rows. What this query do is: get all channels from packages according to client subscription. sometime different packages have same channels and I don't want this channel to be shown twice to client.
USE [DB]
SELECT Videos.number,
Videos.title,
Videos.description,
Videos.format,
Videos.urlTwo AS url,
Videos.icon,
Videos.streamtype,
'pin' AS password,
Videos.categoryid,
Videos.videotype,
FROM subscription
INNER JOIN packages_channels
ON subscription.pkgid = packages_channels.package_id
INNER JOIN Videos
ON packages_channels.video_id = Videos.number
WHERE (GETDATE() >= subscription.startdate)
AND (GETDATE() <= subscription.enddate)
AND (subscription.username = 'mon1')
GROUP BY videos.number, Videos.title
ORDER BY Videos.number asc
this is what it shows right now:
number title description format url icon
101 channel1 101 101 URLstream1 iconPath1
101 channel1 101 101 URLstream1 iconPath1
102 channel2 101 102 URLstream2 iconPath2
107 channel7 107 107 URLstream7 iconPath7
108 channel8 108 108 URLstream8 iconPath8
but the result i want to get is this:
number title description format url icon
101 channel1 101 101 URLstream1 iconPath1
102 channel2 101 102 URLstream2 iconPath2
107 channel7 107 107 URLstream7 iconPath7
108 channel8 108 108 URLstream8 iconPath8
as you see channel1 its only once
url,and icon is the problem. this fields are text
I can't show a real sql result for security reason.
I don't think you really need a group by here since you are not using any aggregate function.
Using distinct will hopefully do the job like below
SELECT DISTINCT
Videos.number,
Videos.title,
Videos.description,
Videos.format,
convert(varchar(max),Videos.urlTwo) AS url,
convert(varchar(max),Videos.icon) as icon,
Videos.streamtype,
'pin' AS password,
Videos.categoryid,
Videos.videotype
FROM subscription
INNER JOIN packages_channels
ON subscription.pkgid = packages_channels.package_id
INNER JOIN Videos
ON packages_channels.video_id = Videos.number
WHERE (GETDATE() >= subscription.startdate)
AND (GETDATE() <= subscription.enddate)
AND (subscription.username = 'mon1')
ORDER BY Videos.number asc
If the other columns such as description and format are functionally dependant on the column you want to group on, you can simply include them in the group by clause.
Functional Dependancy means that the column will always have the same value for a certain value of the column it is functionally dependant on.
If they are not functionally dependant, then this simply will not work, because if you have different descriptions for one number, how is the SQL Server to determine which description to display?
One way to overcome this is by using MAX(Description) for instance. This makes an arbitrary choice for the description that happens to come last alphabetically.

SQL query to turn audit trail in / out times to list of locations

I have a database that is used to track the location of physical objects, lets call them widgets. It has an audit trail table that tracks when a widget is put in a location, and when it leaves a location (and where it went after that).
So conceptually it looks like this
Widget ID Date Old Location New Location
1 01-Oct-2013 NULL 101
1 03-Oct-2013 101 108
1 08-Oct-2013 108 101
2 01-oct-2013 NULL 101
2 02-Oct-2013 101 103
3 12-oct-2013 NULL 101
I want to be able to query a list of which widgets were in location 101 between a start and end date, such as 08-09 Oct 2013, this should be widget 1 but not widget 2 or 3.
I'm not sure how to get all these cases. I can pull a list of widget's that were moved in before the end, and a list of widgets that were moved out before the start, but that would also eliminate widget 1 as it leaves and comes back.
I think I need to convert this to a table with widget, location, entry date and exit date, but I'm not sure how to do that ?
EDIT: As pointed out, My data was wrong, I've updated to make the question the 8th to 9th (it was the 4th to 5th). So Widget 1 is the only widget in location 101 in that period.
Try something like this:
select *
from
(select "Widget ID" id,
"New Location" loc,
"Date" start_date,
lead("Date", 1, sysdate) over (partition by "Widget ID" order by "Widget ID") end_date
from widgets) t
where t.loc = 101
and start_date < <<your_ending_date>> and end_date > <<your_starting_date>>
here is a sqlfiddle demo (note that I changed you data a little bit)
So you need last state of each widget within period.
Probably need subselect statement that selects all widgets between dates, groups them by id, orders by Date desc, selects top 1, so you know widget's last state within the period.
UPDATE according to new conditions
I want to know if the widget was in the location at any time during
the period
You make select with distinct IDs and a subselect with EXISTS that checks if the row with the current ID and date within period and new location = X presents in resultset. This will make you know what items came to store at least 1 time.