I have a query that looks like this:
select Item_Number,Size_Description from Style_Size
where Company_Code='01' and Division_Code='008'
and Item_Number='18SP6726B024B' and Color_Code='00605'
This is the table that it returns
+-----------------+------------------+
| Item_Number | Size_Description |
+-----------------+------------------+
| 18SP6726B024B | SMALL |
| 18SP6726B024B | MEDIUM |
| 18SP6726B024B | LARGE |
| 18SP6726B024B | X-LARGE |
+-----------------+------------------+
How do I make it so it shows all Size Descriptions on one field so it would look like
18SP6726B024B SMALL /MEDIUM /LARGE/X-LARGE
Is this possible or would it have to be four different fields through a pivot? And if so, how would I make sure it will work for all item numbers?
You really need to consider upgrading from 2008. It has been out of support for several months now. You can use the STUFF FOR XML technique for this. It is just a lot more code and effort than the much simpler STRING_AGG we have in the current versions.
This should work for you given the sample data you provided. But you probably want a different order for the sizes. There was nothing in your sample data to indicate a proper sort order there so I just used the Size_Description. You will either need a better sorting column or deal with the sizes in alphabetical order.
select Item_Number
, STUFF((Select '/' + Size_Description
from Style_Size ss
where ss.Company_Code = s.Company_Code
and ss.Division_Code = s.Division_Code
and ss.Item_Number = s.Item_Number
and ss.Color_Code = s.Color_Code
order by ss.Size_Description --You might want/need a different column to order by instead of the size description
FOR XML PATH('')), 1,1 ,'')
from Style_Size s
where Company_Code = '01'
and Division_Code = '008'
and Item_Number = '18SP6726B024B'
and Color_Code = '00605'
group by s.Item_Number
, s.Company_Code
, s.Division_Code
, s.Color_Code
I think you are looking for STRING_AGG and a GROUP BY
So something like
select Item_Number, STRING_AGG(Size_Description, ' /')
from Style_Size
where Company_Code='01' and Division_Code='008'
and Item_Number='18SP6726B024B' and Color_Code='00605'
GROUP BY Item_Number
Related
I've got a table to store collected data from several energy meters, then I created some views to show data from specific meters only. Now I want to combine those views for an overview of only interesting data.
As far as I understood from reading other questions, (where my question here could be a possible duplicate?) JOIN would be what I need and that creates new columns, but the columns with the values of the meters get merged. I guess this is because the columns with the interesting values have all the exact same name, but that is not what I want. I want the colums with the interesting values (named "1.8.0") not merged but in seperate columns as they are in the views, just next to each other for a better overview.
To shorten the post I created following example to show my problem:
http://sqlfiddle.com/#!17/a886d/31 (and maybe also http://sqlfiddle.com/#!17/a886d/30 )
The related query:
SELECT public.meter354123."0.9.2" AS datestamp,
public.meter354123."1.8.0" AS meter354123
FROM public.meter354123
FULL JOIN public.meter354124 ON public.meter354123."1.8.0" = public.meter354124."1.8.0";
For some reason I do not understand yet, the JOIN does not work for me as I would expect. If I JOIN ON the values (column "1.8.0") I get NULL rows, if I JOIN ON the datestamps (column "0.9.2"), one column is missing completely in the result.
(if it is meaningful, feel free to edit the code from the fiddle here into the question, I thought it would be too much code to paste here and I don't know how to explain my issue more simpler)
In the end I would like to have a result like:
| datestamp (=col "0.9.2") | meterdata1 (=col "1.8.0") | meterdata2 (=col "1.8.0") | etc...
| 1220101 | value1 | value1 | ...
| 1220201 | value2 | value2 | ...
| 1220301 | value3 | value3 | ...
Maybe the intermediate views are not necessary at all and it is even possible to pull off this result from the original table without going through those views?
I'm not a database expert so I went with my current knowledge to accomplish that.
Thank you very much for looking into this and for any hints!
You could aggregate meter data into a CSV:
SELECT
"0.9.2" AS datestamp,
string_agg("1.8.0", ',') AS meterdata
FROM public.meter354123
GROUP BY "0.9.2"
Or to get an actual array:
SELECT
"0.9.2" AS datestamp,
array_agg("1.8.0") AS meterdata
FROM public.meter354123
GROUP BY "0.9.2"
Thank you for looking into this, I could "solve" this by using another several intermediate views and then simple JOIN-ing those views as following:
see fiddle: http://sqlfiddle.com/#!17/a886d/40
CREATE VIEW meter354123 AS SELECT meterdata."0.0.0",
meterdata."0.9.1",
meterdata."0.9.2",
meterdata."1.8.0"
FROM meterdata
WHERE meterdata."0.0.0" = 354123::numeric AND meterdata."0.9.1" = 0::numeric
ORDER BY meterdata."0.0.0", meterdata."0.9.2" DESC
LIMIT 12;
CREATE VIEW meter354124 AS SELECT meterdata."0.0.0",
meterdata."0.9.1",
meterdata."0.9.2",
meterdata."1.8.0"
FROM meterdata
WHERE meterdata."0.0.0" = 354124::numeric AND meterdata."0.9.1" = 0::numeric
ORDER BY meterdata."0.0.0", meterdata."0.9.2" DESC
LIMIT 12;
CREATE VIEW meter354127 AS SELECT meterdata."0.0.0",
meterdata."0.9.1",
meterdata."0.9.2",
meterdata."1.8.0"
FROM meterdata
WHERE meterdata."0.0.0" = 354127::numeric AND meterdata."0.9.1" = 0::numeric
ORDER BY meterdata."0.0.0", meterdata."0.9.2" DESC
LIMIT 12;
CREATE VIEW "meter354123_1.8.0" AS SELECT public.meter354123."0.9.2" AS datestamp,
public.meter354123."1.8.0" AS meter354123
FROM public.meter354123
ORDER BY datestamp DESC
LIMIT 12;
CREATE VIEW "meter354124_1.8.0" AS SELECT public.meter354124."0.9.2" AS datestamp,
public.meter354124."1.8.0" AS meter354124
FROM public.meter354124
ORDER BY datestamp DESC
LIMIT 12;
CREATE VIEW "meter354127_1.8.0" AS SELECT public.meter354127."0.9.2" AS datestamp,
public.meter354127."1.8.0" AS meter354127
FROM public.meter354127
ORDER BY datestamp DESC
LIMIT 12;
SELECT "meter354123_1.8.0".datestamp,
"meter354123_1.8.0".meter354123,
"meter354124_1.8.0".meter354124,
"meter354127_1.8.0".meter354127
FROM "meter354123_1.8.0"
JOIN "meter354124_1.8.0" ON "meter354123_1.8.0".datestamp = "meter354124_1.8.0".datestamp
JOIN "meter354127_1.8.0" ON "meter354123_1.8.0".datestamp = "meter354127_1.8.0".datestamp;
which results in:
datestamp | meter354123 | meter354124 | meter354127
-----------+-------------+-------------+-------------
1220301 | 11055.66 | 5403.16 | 88556.23
1220201 | 11054.64 | 5399.47 | 88195.41
1220101 | 11053.33 | 5395.27 | 87799.84
I don't know if there is a more efficient/elegant solution, but at least this gives the wanted result.
Overview
I need to build a description field that describes an entity. The data I am working with has the property description split for each individual key in my table. Below is an example of what the data looks like:
+------------+--------------------+----------+
| Key | Desc | Order_Id |
+------------+--------------------+----------+
| 5962417474 | Big Yellow Door | 14775 |
| 5962417474 | Orange Windows | 14776 |
| 5962417474 | Blue Triangle Roof | 14777 |
+------------+--------------------+----------+
Originally, I wrote a query using an aggregate function like so:
SELECT
[P].[KEY],
CONCAT (MIN([P].[Desc]), + ' ' + MAX([P].[Desc])) [PROPERTY_DESCRIPTION]
FROM [dbo].[PROP_DESC] [P]
WHERE [P].[KEY] = '5962417474'
GROUP BY [P].[KEY];
This worked great for two row entries but then I realized what if I have multiple records for a property description? So I wrote the following query to check if I had multiple property descriptions:
SELECT
[P].[KEY], COUNT([P].[KEY])
FROM [dbo].[PROP_DESC] [P]
GROUP BY [P].[KEY]
HAVING COUNT(*) > 2; -- Returns one record which is the above table result.
This gave me back a record with three descriptions so my original query will not work. How can I tackle this problem down when there are multiple fields?
Desired Output
+------------+---------------------------------------------------+----------+
| Key | Desc | Order_Id |
+------------+---------------------------------------------------+----------+
| 5962417474 | Big Yellow Door Orange Windows Blue Triangle Roof | 14775 |
+------------+---------------------------------------------------+----------+
It depends on what SQL language you're using, but you'll want to use some kind of group concat / array agg function. Eg:
SELECT
Key,
STRING_AGG(desc, ', ')
FROM TABLE
GROUP BY Key;
I have solved my problem with the following query for those that have the same problem and do not have access to STRING_AGG which is introduced in SQL Server 2017:
SELECT
[P].[KEY],
[PROPERTY_DESCRIPTION] = STUFF((
SELECT ' ' + [P2].[DESC]
FROM [dbo].[PROP_DESC] [P2]
WHERE [P].[KEY] = [P2].[KEY]
FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)'), 1, 1, '')
FROM [dbo].[PROP_DESC] [P]
WHERE [P].[KEY] = '5962417474'
GROUP BY [P].[KEY]
There are many ways to do it in SQL server:
Below is one way:
SELECT key
,STUFF((SELECT '| ' + CAST(prop_desc AS VARCHAR(MAX)) [text()]
FROM PROP_DESC
WHERE key = t.key
FOR XML PATH(''), TYPE)
.value('.','NVARCHAR(MAX)'),1,2,' ') prop_desc
FROM PROP_DESC t
GROUP BY key
I'm currently trying to find the 3 most popular articles in a database. I want to print out the title and amount of views for each. I know I'll have to join two of the tables together (articles & log) in order to do so.
The articles table has a column of the titles, and one with a slug for the title.
The log table has a column of the paths in the format of /article/'slug'.
How would I join these two tables, filter out the path to compare to the slug column of the articles table, and use count to display the number of times it was viewed?
The correct query used was:
SELECT title, count(*) as views
FROM articles a, log l
WHERE a.slug=substring(l.path, 10)
GROUP BY title
ORDER BY views DESC
LIMIT 3;
If I understood you correctly you just need to join two tables based on one column using aggregation. The catch is that you can't compare them directly but have to use some string functions before.
Assuming a schema like this:
article
| title | slug |
-------------------
| title1 | myslug |
| title2 | myslug |
log
| path |
--------------------------
| /article/'myslug' |
| /article/'unmentioned' |
Try out something like the following:
select title, count(*) from article a join log l where concat('''', a.slug, '''') = substring(l.path, 10) group by title;
For more complex queries it can be helpful to at first write smaller queries which help you to figure out the whole query later. For example just check if the string functions return what you expect:
select substring(l.path, 10) from log l;
select concat('''', a.slug, '''') from article a;
I have a query I need to perform to show search results for a project. What needs to happen, I need to sort the results by the "horsesActiveDate" and this applies to all of them except for any ad with the adtypesID=7. Those results are sorted by date but they must always result after all other ads.
So I will have all my ads in the result set be ordered by the Active Date AND adtypesID != 7. After that, I need all adtypesID=7 to be sorted by Active Date and appended at the bottom of all the results.
I'm hoping to put this in one query instead of two and appending them together in PHP. The way the code is written, I have to find a way to get it all in one query.
So here is my original query which has worked great until I had to ad the adtypesID=7 which has different sorting requirements.
This is the query that exists now that doesn't take into account the adtypesID for sorting.
SELECT
horses.horsesID,
horsesDescription,
horsesActiveDate,
adtypesID,
states.statesName,
horses_images.himagesPath
FROM horses
LEFT JOIN states ON horses.statesID = states.statesID
LEFT JOIN horses_images ON horses_images.himagesDefault = 1 AND horses_images.horsesID = horses.horsesID AND horses_images.himagesPath != ''
WHERE
horses.horsesStud = 0
AND horses.horsesSold = 0
AND horses.horsesID IN
(
SELECT DISTINCT horses.horsesID
FROM horses
LEFT JOIN horses_featured ON horses_featured.horsesID = horses.horsesID
WHERE horses.horsesActive = 1
)
ORDER BY adtypesID, horses.horsesActiveDate DESC
My first thought was to do two queries where one looked for all the ads that did not contain adtypesID=7 and sort those as the query does, then run a second query to find only those ads with adtypesID=7 and sort those by date. Then take those two results and append them to each other. Since I need to get this all into one query, I can't use a php function to do that.
Is there a way to merge the two query results one after the other in mysql? Is there a better way to run this query that will accomplish this sorting?
The Ideal Results would be as below (I modified the column names so they would be shorter):
ID | Description | ActiveDate | adtypesID | statesName | himagesPath
___________________________________________________________________________
3 | Ad Text | 06-01-2010 | 3 | OK | image.jpg
2 | Ad Text | 05-31-2010 | 2 | LA | image1.jpg
9 | Ad Text | 03-01-2010 | 4 | OK | image3.jpg
6 | Ad Text | 06-01-2010 | 7 | OK | image5.jpg
6 | Ad Text | 05-01-2010 | 7 | OK | image5.jpg
6 | Ad Text | 04-01-2010 | 7 | OK | image5.jpg
Any help that can be provided will be greatly appreciated!
I am not sure about the exact syntax in MySQL, but something like
ORDER BY case when adtypesID = 7 then 2 else 1 end ASC, horses.horsesActiveDate DESC
would work in many other SQL dielects.
Note that most SQL dialects allow the order by to not only be a column, but an expression.
This should work:
ORDER BY (adtypesID = 7) ASC, horses.horsesActiveDate DESC
Use a Union to append two queries together, like this:
SELECT whatever FROM wherever ORDER BY something AND adtypesID!=7
UNION
SELECT another FROM somewhere ORDER BY whocares AND adtypesID=7
http://dev.mysql.com/doc/refman/5.0/en/union.html
I re-wrote your query as:
SELECT h.horsesID,
h.horsesDescription,
h.horsesActiveDate,
adtypesID,
s.statesName,
hi.himagesPath
FROM HORSES h
LEFT JOIN STATES s ON s.stateid = h.statesID
LEFT JOIN HORSES_IMAGES hi ON hi.horsesID = h.horsesID
AND hi.himagesDefault = 1
AND hi.himagesPath != ''
LEFT JOIN HORSES_FEATURED hf ON hf.horsesID = h.horsesID
WHERE h.horsesStud = 0
AND h.horsesSold = 0
AND h.horsesActive = 1
ORDER BY (adtypesID = 7) ASC, h.horsesActiveDate DESC
The IN subquery, using a LEFT JOIN and such, will mean that any horse record whose horsesActive value is 1 will be returned - regardless if they have an associated HORSES_FEATURED record. I leave it to you for checking your data to decide if it should really be an INNER JOIN. Likewise for the STATES table relationship...
I need to make a rather complex query, and I need help bad. Below is an example I made.
Basically, I need a query that will return one row for each case_id where the type is support, status start, and date meaning the very first one created (so that in the example below, only the 2/1/2009 John's case gets returned, not the 3/1/2009). The search needs to be dynamic to the point of being able to return all similar rows with different case_id's etc from a table with thousands of rows.
There's more after that but I don't know all the details yet, and I think I can figure it out if you guys (an gals) can help me out here. :)
ID | Case_ID | Name | Date | Status | Type
48 | 450 | John | 6/1/2009 | Fixed | Support
47 | 450 | John | 4/1/2009 | Moved | Support
46 | 451 | Sarah | 3/1/2009 | |
45 | 432 | John | 3/1/2009 | Fixed | Critical
44 | 450 | John | 3/1/2009 | Start | Support
42 | 450 | John | 2/1/2009 | Start | Support
41 | 440 | Ben | 2/1/2009 | |
40 | 432 | John | 1/1/2009 | Start | Critical
...
Thanks a bunch!
Edit:
To answer some people's questions, I'm using SQL Server 2005. And the date is just plain date, not string.
Ok so now I got further in the problem. I ended up with Bliek's solution which worked like a charm. But now I ran into the problem that sometimes the status never starts, as it's solved immediately. I need to include this in as well. But only for a certain time period.
I imagine I'm going to have to check for the case table referenced by FK Case_ID here. So I'd need a way to check for each Case_ID created in the CaseTable within the past month, and then run a search for these in the same table and same manner as posted above, returning only the first result as before. How can I use the other table like that?
As usual I'll try to find the answer myself while waiting, thanks again!
Edit 2:
Seems this is the answer. I don't have access to the full DB yet so I can't fully test it, but it seems to be working with the dummy tables I created, to continue from Bliek's code's WHERE clause:
WHERE RowNumber = 1 AND Case_ID IN (SELECT Case_ID FROM CaseTable
WHERE (Date BETWEEN '2007/11/1' AND '2007/11/30'))
The date's screwed again but you get the idea I'm sure. Thanks for the help everyone! I'll get back if there're more problems, but I think with this info I can improvise my way through most of the SQL problems I currently have to deal with. :)
Maybe something like:
select Case_ID, Name, MIN(date), Status, Type
from table
where type = 'Support'
and status = 'Start'
group by Case_ID, Name, Status, Type
EDIT: You haven't provided a lot of details about what you really want, so I'd suggest that you read all the answers and choose one that suits your problem best. So far I'd say that Tomalak's answer is closest to what you're looking for...
SELECT
c.ID,
c.Case_ID,
c.Name,
c.Date,
c.Status,
c.Type
FROM
CaseTable c
WHERE
c.Type = 'Support'
AND c.Status = 'Start'
AND c.Date = (
SELECT MIN(Date)
FROM CaseTable
WHERE Case_ID = c.Case_ID AND Type = c.Type AND Status = c.Status)
/* GROUP BY only needed when for a given Case_ID several rows
exist that fulfill the WHERE clause */
GROUP BY
c.ID,
c.Case_ID,
c.Name,
c.Date,
c.Status,
c.Type
This query benefits greatly from indexes on the Case_ID, Date, Status and Type columns.
Added value though the fact that the filter on Support and Status only needs to be set in one place.
As an alternative to the GROUP BY clause, you can do SELECT DISTINCT, which would increase readability (this may or may not affect overall performance, I suggest you measure both variants against each other). If you are sure that for no Case_ID in your table two rows exist that have the same Date, you won't need GROUP BY or SELECT DISTINCT at all.
In SQL Server 2005 and beyond I would use Common Table Expressions (CTE). This offers lots of possibilities like so:
With ResultTable (RowNumber
,ID
,Case_ID
,Name
,Date
,Status
,Type)
AS
(
SELECT Row_Number() OVER (PARTITION BY Case_ID
ORDER BY Date ASC)
,ID
,Case_ID
,Name
,Date
,Status
,Type
FROM CaseTable
WHERE Type = 'Support'
AND Status = 'Start'
)
SELECT ID
,Case_ID
,Name
,Date
,Status
,Type
FROM ResultTable
WHERE RowNumber = 1
Don't apologize for your date formatting, it makes more sense that way.
SELECT ID, Case_ID, Name, MIN(Date), Status, Type
FROM caseTable
WHERE Type = 'Support'
AND status = 'Start'
GROUP BY ID, Case_ID, Name, Status, Type