简体   繁体   中英

SQL - MS Access - if statement

I have the following table:

Table1
Color  Room type
Black  Large
White  Large
White  Small
Black  Medium
Black  Small

I want to run a query that:

  • If the data shows White occurs more often than Black for colors of large rooms then the result would be White
  • If the data shows Black occurs more often than White then the result would be black.
  • If the data shows Black occurs the same amount as White then the result would be unidentified.

Notice that my query is more complicated than that and it has other columns and many tables, but I just want to add the column that specifies if large rooms are mostly white or black.

For all types of rooms:

SELECT RoomType
     , Count( IIF(Color="White",1,NULL) ) AS CountWhite
     , Count( IIF(Color="Black",1,NULL) ) AS CountBlack
     , IIF(   Count( IIF(Color="White",1,NULL) )
            > Count( IIF(Color="Black",1,NULL) ) 
                , "White"
       , IIF(   Count( IIF(Color="White",1,NULL) )
              = Count( IIF(Color="Black",1,NULL) ) 
                , NULL
                , "Black"
            )
          )
       AS Result
FROM Rooms
GROUP BY RoomType

For "Large" rooms only:

SELECT IIF(   Count( IIF(Color="White",1,NULL) )
            > Count( IIF(Color="Black",1,NULL) ) 
                , "White"
       , IIF(   Count( IIF(Color="White",1,NULL) )
              = Count( IIF(Color="Black",1,NULL) ) 
                , NULL
                , "Black"
            )
          )
       AS Result
FROM Rooms
WHERE RoomType = "Large"

Since your question is not yet clear, we have to make some fairly sweeping guesses about what you are after. If your table is really more like:

Rooms
RoomID Color  Room type
  1    Black  Large
  2    White  Large
  3    White  Small
  4    Black  Medium
  5    Black  Small
  6    Black  Large
  7    White  Large
  8    White  Small
  9    White  Large
 10    White  Large

And if you want to know whether there are more black or white rooms of each of the three sizes, and you specify something for 'there are an equal number of black and white rooms' (grey?), and if MS Access can use this syntax, then maybe:

Number of rooms of each colour at each size:

SELECT [Room type] AS RoomType, Color, COUNT(*) AS RoomCount
  FROM Rooms
 GROUP BY RoomType, Color

Self-join this result to get black and white counts into single table:

SELECT NVL(Black.RoomType, White.RoomType) AS RoomType,
       NVL(WhiteCount, 0) AS WhiteCount,
       NVL(BlackCount, 0) AS BlackCount
  FROM (SELECT [Room type] AS RoomType, COUNT(*) AS WhiteCount
          FROM Rooms
         WHERE Color = 'White'
         GROUP BY RoomType
       ) AS White
  FULL OUTER JOIN
       (SELECT [Room type] AS RoomType, COUNT(*) AS BlackCount
          FROM Rooms
         WHERE Color = 'Black'
         GROUP BY RoomType
       ) AS Black
       ON Black.RoomType = White.RoomType

(I don't know if MS Access supports FULL OUTER JOIN. If not, you have to work harder. Similarly, I've not checked whether it supports NVL - maybe it supports COALESCE instead.)

This will give you a series of rows, one for each room type, with a WhiteCount and a BlackCount.

Now you just need to post-process that to give you the information you are after - if my guesswork is justified.


Edit: Must use NVL() or equivalent on RoomType and include table aliases; now tested and producing the correct answer on MacOS X 10.6.7 with IBM Informix Dynamic Server 11.70.FC1:

Roomtype   Whitecount   Blackcount
Large      4            2
Small      2            1
Medium     0            1

If MS Access does not support FULL OUTER JOIN, then the query is very much messier. Does it support LEFT and RIGHT (OUTER) JOIN? Let's hope so because then you can use this (which has been changed to assume that the column name is Rooms.RoomType (no space in the name):

SELECT NVL(White.RoomType, Black.RoomType) AS RoomType,
       NVL(WhiteCount, 0) AS WhiteCount,
       NVL(BlackCount, 0) AS BlackCount
  FROM (SELECT RoomType AS RoomType, COUNT(*) AS WhiteCount
          FROM Rooms
         WHERE Color = 'White'
         GROUP BY RoomType
       ) AS White
  LEFT OUTER JOIN
       (SELECT RoomType AS RoomType, COUNT(*) AS BlackCount
          FROM Rooms
         WHERE Color = 'Black'
         GROUP BY RoomType
       ) AS Black
       ON Black.RoomType = White.RoomType
UNION
SELECT NVL(White.RoomType, Black.RoomType) AS RoomType,
       NVL(WhiteCount, 0) AS WhiteCount,
       NVL(BlackCount, 0) AS BlackCount
  FROM (SELECT RoomType AS RoomType, COUNT(*) AS WhiteCount
          FROM Rooms
         WHERE Color = 'White'
         GROUP BY RoomType
       ) AS White
  RIGHT OUTER JOIN
       (SELECT RoomType AS RoomType, COUNT(*) AS BlackCount
          FROM Rooms
         WHERE Color = 'Black'
         GROUP BY RoomType
       ) AS Black
       ON Black.RoomType = White.RoomType;

This formulation relies on UNION removing duplicate rows. Given the test data, it produces the same result. You could compact it a little by using White.RoomType in the main select-list of the first half of the UNION (instead of the NVL expression) and Black.RoomType in the second half.

If MS Access doesn't support any outer joins, then (a) it's time to upgrade to a real DBMS and (b) let me know and we'll work out the long-handed 3-way UNION way of doing the same job.

As this is Access (ACE, Jet, whatever), we probably need to use intermediary result sets via VIEW s (saved query objects, querydefs, whatever):

CREATE VIEW Table1ColorTallies
(
 Color, tally
)
AS 
SELECT Color, COUNT(*) AS tally
  FROM Table1
 GROUP 
    BY Color;

CREATE VIEW Table1ColorsWithMaxTallies
(
 Color
)
AS 
SELECT T1.Color
  FROM Table1ColorTallies AS T1
 WHERE T1.tally = (
                   SELECT MAX(T2.tally)
                     FROM Table1ColorTallies AS T2
                  );

Using the VIEW s to get the desired result:

SELECT Color
  FROM Table1ColorsWithMaxTallies
 WHERE 1 = (
            SELECT COUNT(*)
              FROM Table1ColorsWithMaxTallies AS T2
           );

Note the above CREATE VIEW SQL DDL must be executed individually in ANSI-92 Query Mode . Alternatively, create them as saved query objects (querydefs, whatever) using the Access UI (DAO, whatever) in the usual way.


Here's some VBA to create a new db called DropMe.mdb in your temp folder, create Table1 with sample data, then create my suggested VIEW s and show the result of my suggested query in a message box. Uses ADO but no references are required (uses late binding). Just paste in any VBA standard code module (eg use Excel) and run. I think it will scale well to more than 1 color;)

Sub BlackAndWhite()

  On Error Resume Next
  Kill Environ$("temp") & "\DropMe.mdb"
  On Error GoTo 0

  Dim cat
  Set cat = CreateObject("ADOX.Catalog")
  With cat
    .Create _
        "Provider=Microsoft.Jet.OLEDB.4.0;" & _
        "Data Source=" & _
        Environ$("temp") & "\DropMe.mdb"
    With .ActiveConnection

      Dim Sql As String

      Sql = _
          "CREATE TABLE Table1" & vbCr & "(" & vbCr & " Color VARCHAR(100)" & _
          " NOT NULL, " & vbCr & " Room_type NVARCHAR(100) NOT" & _
          " NULL, " & vbCr & " UNIQUE (Color, Room_type)" & vbCr & ");"
      .Execute Sql
      Sql = _
          "CREATE VIEW Table1ColorTallies" & vbCr & "(" & vbCr & " Color," & _
          " tally" & vbCr & ")" & vbCr & "AS " & vbCr & "SELECT Color, COUNT(*) AS tally" & vbCr & "" & _
          "  FROM Table1" & vbCr & " GROUP " & vbCr & "    BY Color;"
      .Execute Sql
      Sql = _
          "CREATE VIEW Table1ColorsWithMaxTallies" & vbCr & "(" & vbCr & "" & _
          " Color" & vbCr & ")" & vbCr & "AS " & vbCr & "SELECT T1.Color" & vbCr & "  FROM Table1ColorTallies" & _
          " AS T1" & vbCr & " WHERE T1.tally = (" & vbCr & "            " & _
          "       SELECT MAX(T2.tally)" & vbCr & "           " & _
          "          FROM Table1ColorTallies AS T2" & vbCr & "" & _
          "                  );"
      .Execute Sql
      Sql = _
          "INSERT INTO Table1 (Color, Room_type) VALUES" & _
          " ('Black', 'Large');"
          .Execute Sql
      Sql = _
          "INSERT INTO Table1 (Color, Room_type) VALUES" & _
          " ('White', 'Large');"
          .Execute Sql
      Sql = _
          "INSERT INTO Table1 (Color, Room_type) VALUES" & _
          " ('White', 'Small');"
          .Execute Sql
      Sql = _
          "INSERT INTO Table1 (Color, Room_type) VALUES" & _
          " ('White', 'Med');"
          .Execute Sql
      Sql = _
          "INSERT INTO Table1 (Color, Room_type) VALUES" & _
          " ('Black', 'Small');"
          .Execute Sql

      Sql = _
          "SELECT Color" & vbCr & "  FROM Table1ColorsWithMaxTallies" & vbCr & "" & _
          " WHERE 1 = (" & vbCr & "            SELECT COUNT(*)" & vbCr & "" & _
          "              FROM Table1ColorsWithMaxTallies" & _
          " AS T2" & vbCr & "           );"

      Dim rs
      Set rs = .Execute(Sql)
      MsgBox rs.GetString

    End With
    Set .ActiveConnection = Nothing
  End With
End Sub

You can try the following implementation:

SELECT Table1.[Room Type], 
        Count(IIf(Table1.Color="White",1,Null)) AS CountWhite, 
        Count(IIf(Table1.Color="Black",1,Null)) AS CountBlack, 
        IIf(CountWhite>CountBlack,"White",(IIf(CountBlack>CountWhite,"Black","Tie"))) AS Expr1
 FROM Table1
 GROUP BY Table1.[Room Type];

I think something like the following would accomplish what you described:

SELECT RoomType, 
IIF(BlackCount>WhiteCount, "Black", IIF(BlackCount<WhiteCount, "White", "Unidentified")) AS PrevailingColor

FROM
(SELECT RoomType, SUM(IIF(Color="Black",1,0)) AS BlackCount, SUM(IIF(Color="White",1,0)) AS WhiteCount 
FROM Table1
GROUP BY RoomType)
;

Start with the following query in MS Access. In my example, I've saved it with the name qryRoomColorCounts:

SELECT 
tblRooms.RoomType, tblRooms.Color, Count(*) AS ColorCount 
FROM tblRooms 
GROUP BY tblRooms.RoomType, tblRooms.Color

You can then use this SQL to get your result:

SELECT 
tblRooms.RoomType, 
(SELECT TOP 1 qryRoomColorCounts.Color FROM qryRoomColorCounts WHERE
 qryRoomColorCounts.RoomType=tblRooms.RoomType 
 ORDER BY qryRoomColorCounts.ColorCount DESC) AS MostUsedColor 
FROM tblRooms 
GROUP BY tblRooms.RoomType

I can't get your suggestion to work, can you debug please: no references are required (uses late binding), just paste into any VBA standard code module (eg use Excel) and run:

Sub NotSoBlackAndWhite()

  On Error Resume Next
  Kill Environ$("temp") & "\DropMe2.mdb"
  On Error GoTo 0

  Dim cat
  Set cat = CreateObject("ADOX.Catalog")
  With cat
    .Create _
        "Provider=Microsoft.Jet.OLEDB.4.0;" & _
        "Data Source=" & _
        Environ$("temp") & "\DropMe2.mdb"
    With .ActiveConnection

      Dim Sql As String

      Sql = _
          "CREATE TABLE tblRooms" & vbCr & "(" & vbCr & " Color VARCHAR(100)" & _
          " NOT NULL, " & vbCr & " RoomType NVARCHAR(100) NOT" & _
          " NULL, " & vbCr & " UNIQUE (Color, RoomType)" & vbCr & ");"
      .Execute Sql
      Sql = _
          "CREATE VIEW qryRoomColorCounts AS " & vbCr & "SELECT" & _
          " " & vbCr & "tblRooms.RoomType, tblRooms.Color, Count(*)" & _
          " AS ColorCount " & vbCr & "FROM tblRooms " & vbCr & "GROUP BY" & _
          " tblRooms.RoomType, tblRooms.Color"
      .Execute Sql
      Sql = _
          "INSERT INTO tblRooms (Color, RoomType) VALUES" & _
          " ('Black', 'Large');"
          .Execute Sql
      Sql = _
          "INSERT INTO tblRooms (Color, RoomType) VALUES" & _
          " ('White', 'Large');"
          .Execute Sql
      Sql = _
          "INSERT INTO tblRooms (Color, RoomType) VALUES" & _
          " ('White', 'Small');"
          .Execute Sql
      Sql = _
          "INSERT INTO tblRooms (Color, RoomType) VALUES" & _
          " ('White', 'Med');"
          .Execute Sql
      Sql = _
          "INSERT INTO tblRooms (Color, RoomType) VALUES" & _
          " ('Black', 'Small');"
          .Execute Sql

      Sql = _
          "SELECT " & vbCr & "tblRooms.RoomType, " & vbCr & "(SELECT TOP" & _
          " 1 qryRoomColorCounts.Color FROM qryRoomColorCounts" & _
          " WHERE" & vbCr & " qryRoomColorCounts.RoomType=tblRooms.RoomType" & _
          " " & vbCr & " ORDER BY qryRoomColorCounts.ColorCount" & _
          " DESC) AS MostUsedColor " & vbCr & "FROM tblRooms " & vbCr & "GROUP" & _
          " BY tblRooms.RoomType"

      Dim rs
      Set rs = .Execute(Sql)  ' <--- ERROR HERE '
      MsgBox rs.GetString

    End With
    Set .ActiveConnection = Nothing
  End With
End Sub

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM