0

I have two tables

Table 1:

    -----------------------------------------------
    |  iFirstID  |  fkSomeID  |  cText  | bStatus |
    |     1      |     59     |  'blah' |    1    |
    |     2      |     100    |  'text' |    0    |
    -----------------------------------------------

Table 2:

    -------------------------------------------------------------------------
    |  iSecondID  |   fkFirstID  |   fkOtherID  |  cSomeText  |  bSomeBool  |
    |      1      |       1      |       1      |    "text"   |      1      |
    |      2      |       1      |       2      | "more text" |      0      |
    |      3      |       1      |       3      | "more text" |      0      |
    |      4      |       2      |       1      |   "blah"    |      1      |
    |      5      |       2      |       2      |   "test"    |      1      |
    |      6      |       2      |       3      |   "data"    |      0      |
    -------------------------------------------------------------------------

I'd like to write a query which will return the following dataset based on the above

Result:

    -----------------------------------------------------------------------------------------------------
    |  fkFirstID  |  fkSomeID  |  cText  |  bSomeBool[1]  |  bSomeBool[2]  |  bSomeBool[3]  |  bStatus  |   
    |      1      |     59     |  'blah' |      1         |       0        |        0       |     0     |
    |      2      |    100     |  'text' |      1         |       1        |        0       |     0     |
    -----------------------------------------------------------------------------------------------------

As you can see in the above, bSomeBool will be reported for every row in table 2 with the same fkFirstID.

I'm not familiar with pivots at all, but am pretty sure this is what I'll need to use to accomplish what I'm looking for. I've looked at a few suggestions online (like this one: SQL server join tables and pivot) but can't wrap my head around it, as most differ a little from what I'm trying to accomplish.

There is a third table not included which is where fkOtherID comes into play. We can assume this table to have two columns: iOtherID and cColumnName

In Table 2, we will never have a duplicate fkOtherID for the same fkFirstID. So for each fkOtherID value, we would go to table 3, and set the column name as cColumnName. Not sure if this will affect the final solution too much.

I've tried using an inner join, but the result was that it would only look at the first row of Table 2.

I tried using Group By, but it kept returning errors for my other columns as they weren't a part of an aggregate function, but even still I don't think this would be what I want as I think it'd return two rows, one with a 1, and the other with a 0 for the bSomeBool column.

an0n
  • 3
  • 2
  • what happens if `fkFirstID` is found more than 3 times? Are you going to continue to add more columns? – Ryan Wilson Dec 05 '22 at 20:27
  • That would be ideal, but generally the number of fkFirstID will stay the same. – an0n Dec 05 '22 at 20:31
  • I don't think you want a pivot. When using PIVOT, the data values become the column names. You aren't doing that, rather, you are assigning arbitrary column names. I just answered a similar question where I created a dynamic SELECT statement that accomplished the same thing. See https://stackoverflow.com/questions/74691637/sql-server-move-data-from-multiple-rows-to-one-row-to-new-columns-and-assign-co/74692778#74692778. – Tim Jarosz Dec 05 '22 at 20:32
  • I didn't include this detail as I didn't think it would be relevant to the question, but I did include the column. Would a pivot be possible if the names were accessible with fkOtherID in a third table? We could assume it to just have two columns, iOtherID and cColumnName - Will look at your answer on the other question now. There would never be a duplicate value in fkOtherID. If there were 3 values (1, 2, 3), it would go in that order for every fkFirstID – an0n Dec 05 '22 at 20:37
  • Does this answer your question? [SQL Server: move data from multiple rows to one row to new columns and assign column name](https://stackoverflow.com/questions/74691637/sql-server-move-data-from-multiple-rows-to-one-row-to-new-columns-and-assign-co) – June7 Dec 05 '22 at 20:50
  • @TimJarosz above had included a link to that, and I'm looking through it. I'm not sure if that would work in my case. I'm currently trying to rewrite it for my own tables but I'm finding that in itself difficult. – an0n Dec 05 '22 at 20:52
  • In case you find it of interest, here is how it could be done in Access https://stackoverflow.com/questions/64400911/pivot-query-in-ms-access. What are you using to program GUI? – June7 Dec 05 '22 at 21:01

1 Answers1

0

As I said in my comment, it's more of a dynamic SELECT problem than a PIVOT problem. Here's how I would do it with dynamic SQL.

CREATE TABLE dataOne (
  iFirstID int
  , fkSomeID int
  , cText nvarchar(50)
  , bStatus int
);

INSERT INTO dataOne (iFirstID, fkSomeID, cText, bStatus)
VALUES 
  (1,59,'blah',1)
  , (2,100,'text',0)
;

CREATE TABLE dataTwo (
  iSecondID int
  , fkFirstID int
  , fkOtherID int
  , cSomeText nvarchar(50)
  , bSomeBool int
);

INSERT INTO dataTwo (iSecondID, fkFirstID, fkOtherID, cSomeText, bSomeBool)
VALUES
  (1,1,1,'text',1)
  , (2,1,1,'more text',0)
  , (3,1,1,'more text',0)
  , (4,2,1,'blah',1)
  , (5,2,1,'test',1)
  , (6,2,1,'data',0)
;

--Get the maximum number of events so we know how many columns to create.
DECLARE @maxBools int;
SET @maxBools = (
  SELECT MAX(d2.row_num)
  FROM (
    SELECT ROW_NUMBER() OVER(PARTITION BY fkFirstID ORDER BY fkOtherID) as row_num
    FROM dataTwo
  ) as d2 );

--Create a variable to hold the dynamic query.
DECLARE @sqlText nvarchar(max);

--Create the first part of the SELECT statment with place holders
--where we can insert columns ({{0}}) and JOINS ({{1}})
SET @sqlText = '
  WITH prelim as (
    SELECT *, ROW_NUMBER() OVER(PARTITION BY fkFirstID ORDER BY fkOtherID) as row_num
    FROM dataTwo
  )
  SELECT DISTINCT d1.iFirstID, d1.fkSomeID, d1.cText, d1.bStatus
    {{0}}
  FROM dataOne as d1
    {{1}}
  ';

--Setup loop variables.
DECLARE @loopCount int = 1;
DECLARE @loopCText nvarchar(50);

WHILE (@loopCount <= @maxBools)
BEGIN
  --Get a string for the loop count... pad it with zeros if multi-digits.
  SET @loopCText = RIGHT(
    '0000000000' + CAST(@loopCount as nvarchar(10))
    , LEN(CAST(@maxBools as nvarchar(50)))
  );

  --Add a SELECT entry for this loop instance.
  SET @sqlText = REPLACE(@sqlText, '{{0}}', '
    , d2' + @loopCText + '.bSomeBool as bSomeBool' + @loopCText + '
    {{0}}
  ');

  --Add a JOIN entry for this loop instance.
  SET @sqlText = REPLACE(@sqlText, '{{1}}', '
    LEFT OUTER JOIN prelim as d2' + @loopCText + '
      ON d2' + @loopCText + '.fkFirstID = d1.iFirstID
      AND d2' + @loopCText + '.row_num = ' + CAST(@loopCount as nvarchar(10)) + '
    {{1}}
  ');

  --SELECT @sqlText;  --For debugging
  SET @loopCount = @loopCount + 1;
END

--Post Loop Cleanup... remove template place holders.
SET @sqlText = REPLACE(@sqlText, '{{0}}', '');
SET @sqlText = REPLACE(@sqlText, '{{1}}', '');

EXEC(@sqlText);




iFirstID fkSomeID cText bStatus bSomeBool1 bSomeBool2 bSomeBool3
1 59 blah 1 1 0 0
2 100 text 0 1 1 0

fiddle

Tim Jarosz
  • 1,133
  • 1
  • 8
  • 15
  • Thank you! I've implemented this into my own application and it appears to be working. Just going to run a few more tests make sure it's completely understood and then I will mark as answer :) – an0n Dec 05 '22 at 21:03