GROUP BY query text columns - sql

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.

Related

Joins Specific Querying

I'm using SQL Server 2017 and I have a question on how to return specific values (I'm not sure 100% if I should be using joins or subqueries.) I tried to put an example below of what I'm trying to do and hopefully someone can help me out with this.
I have two tables below:
Table 1
Record
Topic
100
History
101
Science
102
Art
103
Music
Table 2
Record
Course
100
Intro
100
Intermediate
101
Intermediate
101
Advanced
102
Intro
102
Intermediate
103
Intermediate
103
Advanced
I want to join the two tables together, but only bring back the columns 'Record' and 'Topic' ONLY where the Record DOES NOT have an 'Intro' course attached to it.
So I would want to bring back the following results (since both Courses do not have an Intro class):
Record
Topic
101
Science
103
Music
So far I've had no luck and am bringing back more records than I should so any help or guidance would be appreciated. Thank you in advance.
You could use exists logic here:
SELECT Record, Topic
FROM Table1 t1
WHERE NOT EXISTS (
SELECT 1
FROM Table2 t2
WHRE t2.Record = t1.Record AND
t2.Course = 'Intro'
);

DB2 Select from two tables when one table requires sum

In a DB2 Database, I want to do the following simple mathematics using a SQL query:
AvailableStock = SupplyStock - DemandStock
SupplyStock is stored in 1 table in 1 row, let's call this table the Supply table.
So the Supply table has this data:
ProductID | SupplyStock
---------------------
109 10
244 7 edit: exclude this product from the search
DemandStock is stored in a separate table Demand, where demand is logged as each customer logs demand during a customer order journey. Example data from the Demand table:
ProductID | DemandStock
------------------------
109 1
244 4 edit: exclude this product
109 6
109 2
So in our heads, if I want to calculate the AvailableStock for product '109', Supply is 10, Demand for product 109 totals to 9, and so Available stock is 1.
How do I do this in one select query in DB2 SQL?
The knowledge I have so far of some of the imagined steps in PseudoCode:
I select SupplyStock where product ID = '109'
I select sum(DemandStock) where product ID = '109'
I subtract SupplyStock from DemandStock
I present this as a resulting AvailableStock
The results will look like this:
Product ID | AvailableStock
109 9
I'd love to get this selected in one SQL select query.
Edit: I've since received an answer (that was almost perfect) and realised the question missed out some information.
This information:
We need to exclude data from products we don't want to select data for, and we also need to specifically select product 109.
My apologies, this was omitted from the original question.
I've since added a 'where' to select the product and this works for me. But for future sake, perhaps the answer should include this information too.
You do this using a join to bring the tables together and group by to aggregate the results of the join:
select s.ProductId, s.SupplyStock, sum(d.DemandStock),
(s.SupplyStock - sum(d.DemandStock)) as Available
from Supply s left join
Demand d
on s.ProductId = d.ProductId
where s.ProductId = 109
group by s.ProductId, s.SupplyStock;

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.

Comparing data in same table 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

Compare 2 values of different types inside of subquery

I am using a MS SQL db and I have 3 tables: 'base_info', 'messages', 'config'
bases:
ID Name NameNum
====================================
1 Home 101
2 Castle 102
3 Car 103
messages:
ID Signal RecBy HQ
============================
111 120 Home 1
111 110 Castle 1
111 125 Car 1
222 120 Home 2
222 125 Castle 2
222 130 Car 2
333 100 Home 1
333 110 Car 2
config:
ID SignalRec SignalOut RecBy HQ
====================================
111 60 45 101 1
111 40 60 102 1
222 50 60 102 2
222 30 90 101 2
333 80 10 103 1
Ok so now I have a subquery in which I select the 'SignalRec' and 'SignalOut' from the config table and match it on the messages table by ID and Date(not included above), the problem is that I need it to match where messages.RecBy = config.RecBy but config.RecBy is a string but it's equivalent Name is in the bases table. So I almost need to do a subquery inside a subquery or some type of join and compare the returned value.
Here is what I have so far:
(SELECT TOP 1 config.SignalRec from config WHERE config.ID = messages.ID AND ||I need th other comparison here||...Order By...) As cfgSignalRec,
(SELECT TOP 1 config.SignalOut from config WHERE config.ID = messages.ID AND ||I need th other comparison here||...Order By...) As cfgSignalOut
I tried to make this as clear as possible but if you need more info let me know.
I would normalize out RecBy in your messages table to reference the bases table. Why would you insert the string content there if it's also referenced in bases?
This is exactly why normalization exists: reduce redundancy, reduce ambiguity, and enforce referential integrity.
To make this more clear, RecBy in the messages table should be a foreign key to Bases.
I think this could do the trick (although I have not tried it...)
SELECT
c.SignalRec
FROM config c
INNER JOIN bases b
ON c.RecBy = b.NameNum
INNER JOIN messages m
ON b.Name = m.RecBy
WHERE c.ID = m.ID
However, as Anthony pointed out, you probably want to normalize out the strings in the RecBy column in the messages table, as you have the same data in the bases table.
From your description, it just sounds like you need two JOINS
SELECT TOP 1
c.SignalRec
FROM
config c
INNER JOIN
bases b
ON c.RecBy = b.NameNum
INNER JOIN
messages m
ON b.Name = m.RecBy
I think I might have not been clear enough what I wanted to do, sorry about that.
The data is actually different in the 2 tables, although the correlations are the same. It's kind of confusing to explain without going into detail about how the system works.
I actually found a very fast way of doing this.
Inside my sub-query I do this:
(SELECT TOP 1 config.Signal FROM config,bases
WHERE config.ID = messages.ID AND bases.Name = messages.RecBy AND bases.NameNum =
config.RecBy Order By...)
So this essentially compares the 2 RecBy's of different tables even though one is an integer and the other is a string. It reminds me of a match and look up in Excel.