0

I've read many posts with similar questions, but haven't found an answer yet. I'm quite new to SQL. Using SQL Server Express 2008.

My goal is to get a single-column result of all values that are distinct among several columns with similar names. Related columns have the same base name (suffix), followed by an integer. I have many groups of columns, so I don't want to hard-code the query.

Fruit1 Coating1 Temperature1   Fruit2 Coating2  Temperature2 Fruit3 Coating3  Temperature3
-----------------------------------------------------------------------------------
apple  caramel  72.5           pear   chocolate  74.1        apple  chocolate  98.6
pear   caramel  73.3           peach  chocolate  42.7        apple  chocolate  33.0

So I want the server to return, for example, all the fruits used in any/all fruit columns apple peach pear

I already know how to get a list of the column names using a wildcard:

SELECT Column_name AS columnNames
FROM INFORMATION_SCHEMA.COLUMNS
WHERE Column_name LIKE 'Fruit%' 

I also know how to find distinct values from columns if I already know the column names:

SELECT DISTINCT Fruit FROM(
SELECT Fruit1 as Fruit from FruitBasket
UNION
SELECT Fruit2 as Fruit from FruitBasket
UNION
SELECT Fruit3 as Fruit from FruitBasket)
AS finalOutput

What I need to know is how to use the column-name results from the first query in the UNION parameters of the second. Should I use a FOR loop or something?

Thanks!

XKCD137
  • 357
  • 4
  • 14
  • 4
    Any time you have a table with columns such as `col1`, `col2`, `col3`, be sure you have an issue in your DB design. You should work on that, then your query will be a lot easier – Adriano Carneiro Apr 30 '13 at 19:08
  • 1
    You should look at a different data design. Fruit, coatings, sprinkles, and an ID column would make life easier for you. – mikeY Apr 30 '13 at 19:08
  • 1
    this looks like a real bad database design. – worenga Apr 30 '13 at 19:09
  • You will need dynamic SQL. And probably a dynamic `UNPIVOT` – Lamak Apr 30 '13 at 19:10
  • I agree it's a bad design. I don't know how to make it better. Does SQL have 3D tables or anything? – XKCD137 Apr 30 '13 at 19:13
  • http://sqlrelationship.com/many-to-many-relationship/ – valverij Apr 30 '13 at 19:14
  • This is exactly the reason behind normal forms - because if it's not a normal form, you run into problems like this, where you've taken something that should be rows, and made it into a limited number of columns. You've got numerous problems here and this query is just pointing that out for you. If you go forward with this design you will need to do a lot of work-arounds like this. – Jasmine Apr 30 '13 at 19:14
  • XKCD137 - if you change your question I will answer how to fix this design, but an explanation of the correct design here wouldn't answer the question as it currently stands. – Jasmine Apr 30 '13 at 19:15
  • The table I'm working with is a table of events, and has a lot of data about each event. One particular aspect of the event was once singular, but now there are 4 of those things per event. I don't really want to create a new table for those particular aspects for every event, so I'm trying to just replicate 4 sets of those columns. Any advice is appreciated. It's easier for me to do this correctly now than to fix it later. – XKCD137 Apr 30 '13 at 19:18
  • @Jasmine Can you suggest any reading for me or point me to what I should google? I inherited this mess from a "consultant" who was basically a high-schooler with no formal training. I am an embedded firmware engineer trying to learn SQL ASAP. Thank you for your advice :-) – XKCD137 Apr 30 '13 at 19:23
  • So, is a rewrite of the table schema a possibility, or are you still looking to work within the confines described above? – Nick Vaccaro Apr 30 '13 at 19:27
  • A rewrite is possible. – XKCD137 Apr 30 '13 at 19:36
  • If you'd like help rewriting it, please close this and ask a new question. I'd totally recommend the rewrite, btw. – Nick Vaccaro Apr 30 '13 at 19:40
  • Based on your comment, and I mean no insult by this, you need to find someone to design this database for you. Embedded programming involves radically different concepts and ways of thinking from SQL, and while I've seen people manage to do both fairly well, it did take me ten years to get to that point. So, I think the best thing for you to deal, considering your ridiculous time constraint (one month, really?!) - is to have someone design this database for you. You just don't have the skills to do it right at this point - if this was a different site, I would do that for you. – Jasmine Apr 30 '13 at 21:47

3 Answers3

0

Edit:

The user mentioned in a comment that he'd be open to a new design.

Click here for a working example of the below code

I agree it's a bad design. I don't know how to make it better.

Here's how you could improve your design:

    CREATE TABLE fruit
(
  fruit_id INT CONSTRAINT pk_fruit_pid PRIMARY KEY,
  fruit_name VARCHAR(25)
);

CREATE TABLE temperature
(
  temperature_id INT CONSTRAINT pk_sprinkle_pid PRIMARY KEY,
  temperature_value DECIMAL(18,1)
);

CREATE TABLE coating
(
  coating_id INT CONSTRAINT pk_coating_pid PRIMARY KEY,
  coating_name VARCHAR(25)
);

CREATE TABLE fruit_temperature_coating
(
  fruit_id INT CONSTRAINT fk_fruit_fid FOREIGN KEY REFERENCES fruit(fruit_id),
  temperature_id INT CONSTRAINT fk_temp_fid FOREIGN KEY REFERENCES temperature(temperature_id),
  coating_id INT CONSTRAINT fk_coating_fid FOREIGN KEY REFERENCES coating(coating_id)
);

Run the Query:

     SELECT f.fruit_name, 
       t.temperature_value, 
       c.coating_name 
FROM   fruit_temperature_coating AS fpc
       INNER JOIN Fruit AS f 
               ON fpc.fruit_id = f.fruit_id 
       INNER JOIN temperature AS t
               ON fpc.temperature_id = t.temperature_id 
       INNER JOIN coating AS c 
               ON fpc.coating_id = c.coating_id 
Community
  • 1
  • 1
What have you tried
  • 11,018
  • 4
  • 31
  • 45
  • This is an answer to a comment, not the question. – Nick Vaccaro Apr 30 '13 at 19:32
  • @NickVaccaro There's no reason to downvote it. This is an anwswer to how he can solve his problem, just a longer version (which took a lot of time for me to make) – What have you tried Apr 30 '13 at 19:35
  • If he wants to ask the question, a new question, "How can I fix this poor schema?", then yeah, it is a good answer. I'd be happy to upvote it indeed. – Nick Vaccaro Apr 30 '13 at 19:36
  • Thanks Evan. Unfortunately, I may have oversimplified my example. Some of my columns are numeric data types that can't be linked to another table (or is that actually possible?). – XKCD137 Apr 30 '13 at 19:37
  • @XKCD137 They can certainly be linked, as long as you have an ID (or some index) to unique represent each record, as I've shown above, you can certainly do that. – What have you tried Apr 30 '13 at 19:38
0

I agree that the database design should be improved, but if you were handed a giant project and need the data now, you can do some crazy stuff with cursors/dynamic sql/temp tables:

GO

DECLARE @sql varchar(500) = '',
        @currentColumn varchar(50)


CREATE TABLE #fruit (Fruit varchar(20))

DECLARE fruit_cursor CURSOR FOR
    SELECT c.name
    FROM sys.tables t
        INNER JOIN sys.columns c
            on t.object_id = c.object_id
    WHERE t.name = 'TABLE NAME HERE'
    AND c.name like 'Fruit%'

OPEN fruit_cursor

FETCH NEXT FROM fruit_cursor INTO @currentColumn

WHILE @@FETCH_STATUS = 0 BEGIN

    SET @sql = 
        'INSERT INTO #fruit ' +
        'SELECT @columnName ' +
        'FROM TABLE NAME HERE'

    SET @sql = REPLACE(@sql, '@columnName', @currentColumn)

    EXEC(@sql)

    FETCH NEXT FROM fruit_cursor INTO @currentColumn
END

CLOSE fruit_cursor

DEALLOCATE fruit_cursor

SELECT DISTINCT Fruit  
FROM #fruit
ORDER BY Fruit

DROP TABLE #fruit 

For the future, though, I'd highly recommend looking into many-to-many relationships.

valverij
  • 4,871
  • 1
  • 22
  • 35
  • Thanks, Valverij and Evan, How does a many-to-many relationship work with floating-point numeric data? Do I need to add every value to the table and then link it back-and-forth to all the events that use that value? – XKCD137 Apr 30 '13 at 19:48
  • 1
    @Evan Stack Overflow is a question and answer site. If the OP would like to create a new question based on the comments above, he should feel free to do so. However, you don't get bonus points because you think you're answering a better question than the one he asked. Please stay on topic. – Nick Vaccaro Apr 30 '13 at 19:49
  • GUYS, Thank you all for your input. I'm open to any organizational suggestions, but one big fear I have is in complicating my C#.NET code that performs the queries and deals with data readers C# DataTables, fills DataGridViews, etc. I need to have this whole program (GUI, database, communication, etc.) ready to deploy in one month. – XKCD137 Apr 30 '13 at 20:10
0

Thanks to everyone for your advice.

First of all, I have created an ugly, but working, solution to my original question, based on replies I received. It basically executes a query string assembled from another query of similarly-named columns. The output is, indeed, a list of all distinct values from multiple columns with similar names. This code is pasted below.

However, as y'all said, the whole schema is bad. If I try to do anything useful with those results, it becomes very complicated because I need to track which column number a distinct value belonged to. Trying to sort or compare data in correspondingly-numbered columns is even more crazy because I can't be sure if Apples were always put in "Fruit1" and Peaches in "Fruit2", or the other way around, etc.

After sleeping on it, I came up with a better solution, which is to create another, single, table of all "fruit basket" data that (while perhaps not "many-to-many") is doubly-linked from the "event history" row to a row in the "fruit basket" table, so I can easily search through all fruit baskets for particular info and then follow the link back to the event history. This way, if I find out that apples are toxic above 398.2 degrees, I can easily locate all events where apples so hot were sent, or I can sort all events with apples, by temperature.

This will take some rewriting in my application code, but it's better to fix it now. :-)

Here is my code for finding distinct values in similarly-named columns, but I won't be using this in my project after-all, as stated above.

DECLARE @sql varchar(MAX) = 'SELECT DISTINCT FriutBasketInfo FROM(',
    @currentColumn varchar(50)

DECLARE fruit_cursor CURSOR FOR    
    SELECT Column_name AS columnNames
    FROM INFORMATION_SCHEMA.COLUMNS
    --WHERE Column_name LIKE 'Fruit%' 
    --WHERE Column_name LIKE 'Coating%' 
    WHERE Column_name LIKE 'Temperature%'    

OPEN fruit_cursor

FETCH NEXT FROM fruit_cursor INTO @currentColumn
SET @sql = @sql + 'SELECT ' + @currentColumn + ' as FriutBasketInfo from FruitBasketHistoryTable '
FETCH NEXT FROM fruit_cursor INTO @currentColumn

WHILE @@FETCH_STATUS = 0 BEGIN    
    SET @sql = @sql + ' UNION SELECT ' + @currentColumn + ' as FriutBasketInfo from FruitBasketHistoryTable '
    FETCH NEXT FROM fruit_cursor INTO @currentColumn    
END

SET @sql = @sql + ') AS finalOutput'

EXEC(@sql)

CLOSE fruit_cursor
DEALLOCATE fruit_cursor
XKCD137
  • 357
  • 4
  • 14
  • Definitely ugly, but as someone who has had to work under similar deadlines, I'm glad you came up with a solution that makes sense for your situation. – valverij May 01 '13 at 15:06