我想用SQL创建游戏Battleships(但有飞机)。这是基于这个人的想法:SQL Battleship 我已经有一些表格了:
CREATE TABLE [dbo].[Game](
[Round_Num] [int] IDENTITY(1,1) PRIMARY KEY,
[Player_ID] [varchar](10) NULL,
[Grid_Pick] [varchar](2) NULL,
[Outcome] [varchar](6) NULL,
[Plane_Hit] [varchar](10) NULL,
[Helicopter] [varchar](6) NULL DEFAULT ('_'),
[Jet] [varchar](6) NULL DEFAULT ('__'),
[Airbus] [varchar](6) NULL DEFAULT ('___'),
[Rocket] [varchar](6) NULL DEFAULT ('____'),
[Shots_Fired] [int] NULL,
[Shots_Hit] [int] NULL,
[Hit_Pct] [float] NULL,
[Ships_Sunk] [int] NULL,
);
CREATE TABLE [dbo].[Grid_A](
[ID] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY,
[A] [varchar](1) NULL,
[B] [varchar](1) NULL,
[C] [varchar](1) NULL,
[D] [varchar](1) NULL,
[E] [varchar](1) NULL,
[F] [varchar](1) NULL,
[G] [varchar](1) NULL,
[H] [varchar](1) NULL,
);
表Grid_B
看起来和Grid_A
完全一样。
现在,我尝试用飞机填充网格(或者是否需要更改这些网格?)。我有一个8x8的网格,所以我想有4个“飞机”:
如何将它们随机放入网格中?
我尝试过使用此触发器,但没有成功:
CREATE TRIGGER INSERTPLANESB
ON Game AFTER INSERT AS
DECLARE @Rand CHAR;
DECLARE @Random INT;
DECLARE @Upper INT;
DECLARE @Lower INT;
BEGIN
SET @Lower = 1
SET @Upper = 8
SELECT @Random = ROUND(((@Upper - @Lower -1) * RAND() + @Lower), 0)
SELECT @Random
SELECT @Rand = CAST(@Random AS CHAR)
SELECT @Rand
SELECT @Random = ROUND(((@Upper - @Lower -1) * RAND() + @Lower), 0)
SELECT @Random
UPDATE Grid_B
SET @Rand = "H"
WHERE ID = @Random;
END
GO
我想插入每个平面的第一个字母,以便可以看到哪个平面在哪里。我尝试过用H直升机。
答案 0 :(得分:1)
这里发生了很多事情。让我们从这里开始:
UPDATE Grid_B
SET @Rand = "H"
WHERE ID = @Random;
上面的语句是有效的语法,但很容易引起误解,因为它永远不会更新Grid_B
。原因是编写命令的SET
子句是为了更新局部变量@Rand
,而不是更新表中的任何特定列。 Grid_B
服务于此命令的唯一目的是建立一个条件,在该条件下@Rand
的值将发生变化;只要您的表中有一个或多个记录ID = @Random
,它就会设置为“ H”。换句话说,它实际上等效于以下内容:
if exists (select 1 from Grid_B where ID = @Random)
set @Rand = 'H';
我确定那不是您想要的。我的猜测是您想更新Grid_B
中名称与@Rand
的内容相匹配的列-例如,如果@Rand = 'C'
,则想更新Grid_B.C
用于具有给定ID
的记录。要获得这样的效果,您将需要使用dynamic SQL。而且,如果您几乎必须使用动态SQL与该表进行任何形式的交互,那么您可能应该问自己是否具有正确的数据模型。
您将要遇到的下一个问题是@Rand
将永远不会包含Grid_B
中的列名以及编写的代码,因为您要通过采用另一个变量来分配其值(@Random
)包含数字并将其直接转换为char
。也许您在这里的期望是表达式CAST(@Random AS CHAR)
的结果为1表示“ A”,2表示“ B”,依此类推,但这不是事实。如果@Random
包含数字1,则@Rand
最终将包含字符“ 1”。您可以使用CHAR()
function来达到您想要的效果。
继续倒退,您对@Random
的分配也不会做您想要的。这是我正在谈论的代码:
SET @Lower = 1
SET @Upper = 8
SELECT @Random = ROUND(((@Upper - @Lower -1) * RAND() + @Lower), 0)
我假设您想要的是@Random
是1到8之间的一个整数,包括1和8,其中每个结果的可能性均等。实际上,您将得到一个从1到 7 的整数(包括1和7),其中1和7的可能性只有2到6的数字的一半。RAND()
function产生(0, 1)
范围内的一个值-两端都是唯一的。完成后乘以U - L - 1
会将范围转换为(0, U - L - 1)
,然后添加L
会将其转换为(L, U - 1)
或1到7(不包括)。然后,您应用ROUND()
function来获取最接近的整数。 (1, 1.5)
范围内的值将产生1.0,[1.5, 2.5)
范围内的值将产生2.0,依此类推,而[6.5, 7)
范围内的值将产生7.0。因为将产生值2到6的范围是将产生值1和7的范围的两倍,所以数字2到6的出现频率是两倍。如果您想要均匀分布,请考虑使用FLOOR()
function。有关示例,请参见this question。
最后,即使您解决了所有上述问题,您所拥有的只是一些代码,这些代码将为表的随机行的随机列分配一个字母。如果您打算将其用于类似《战舰》的游戏,那么它将要做的还不止这些。例如,您将不得不考虑如何对其进行修改以处理多个平面,如何处理尺寸大于1x1的平面,如何确保您不在一个平面上放置另一个平面等。但是,我猜猜这些是另一天的问题。祝你好运。
答案 1 :(得分:1)
跟进Joe Farrells详细回答,我想添加一些TL; DR点:
在将行插入Grid_A
之前,其中没有行,因此更新不会执行任何操作。如果您当前在Grid_A
中有行,那么在脚本中包含插入语句会更容易
您不使用触发器进行初始填充。您使用存储过程。触发器是“触发”其他数据库活动的。在您的情况下,您正在执行表格的初始填充-您不需要为此触发
认为UPDATE @ColumnName=Something
将更新以'@ColumnName'命名的列是一个常见的错误。那样行不通
因此,进入下一阶段,您需要编写一个表填充存储过程。首先,您编写一个脚本在存储过程中创建代码:
CREATE PROC p_Initilisation
AS
BEGIN
-- This stored procedure populates the grid
-- Clear out any old games
DELETE Grid_A;
-- Fill with blank values
INSERT INTO Grid_A (A,B,C,D,E,F,G,H)
VALUES
('*','*','*','*','*','*','*','*'),
('*','*','*','*','*','*','*','*'),
('*','*','*','*','*','*','*','*'),
('*','*','*','*','*','*','*','*'),
('*','*','*','*','*','*','*','*'),
('*','*','*','*','*','*','*','*'),
('*','*','*','*','*','*','*','*'),
('*','*','*','*','*','*','*','*')
END
运行该命令-它将创建一个名为p_Initilisation
现在,您已经创建了它,然后运行它:
EXEC p_Initilisation
这将填充您的网格
您可以检查以下内容:
SELECT * FROM Grid_A
那是一个开始
这显示了使用表并对其进行初始化的一些基础知识。
但是出于很多的原因,这整个方法都行不通,并且全部回到了数据建模
数据建模是您确定需要存储哪些数据以及将其存储在表格中的最佳形状。
您的Grid_A
表是一个典型的“交叉表”表,它反映了您希望最终数据如何显示,但是用于表示此数据的实际表通常看起来并不像那样
我现在就把它留在那里。希望您对表,触发器,存储过程有所了解。
看起来像这样变成了TL; DR;反正发布!